2025 年了,我不允许有前端不会用 Trae 让页面 Hero Section 变得高级!!!(Threejs)

9,693 阅读24分钟

2025 年了,我不允许有前端不会用 Trae 让页面 Hero Section 变得高级!!!(Threejs)

0.前置条件

hero.jpg 我正在参加Trae「超级体验官」创意实践征文,本文所使用的 Trae 免费下载链接:

www.trae.ai/?utm_source…

您本文章为Threejs以及 Shader(glsl)的微进阶课程,但您只需要对Threejs有大概了解即可。此课程会涉及到 shader的一些撰写,但完全属于入门级别,可放心食用。

1.前言啥是 Hero Section ?

Hero Section是网站的首页顶部区域,通常包含吸引人的图片、标题和简短的描述,用来吸引访问者的注意并引导他们进一步浏览网站内容。这个区域通常是网站最显眼和重要的部分,通过精心设计和内容选择,可以吸引访问者的兴趣,提升用户体验和网站的吸引力。让我们来看今天的 Hero Section ,看看符不符合您的胃口!!!当然源码地址也会在文章结尾处奉上!

在线地址: https://earth-map-hero-section.vercel.app/#debug

01.gif

2.搭建基础环境

本次我们搭建项目的核心框架是 Threejs,作为一个在 github上 Star 数过100k 的知名图形渲染框架。出色的创建和展示 3D 图形功能允许开发者在网页上渲染复杂的 3D 图形和动画,而无需深入了解 WebGL 的底层细节。因出色的设计已经Threejs的新奇交互配合而被awwwards提名的网站更是不计其数。

Threejs 三要素

那么在我们使用之前我们要知道图形渲染框架。即为创建 3D 场景的三要素

要素作用
场景场景是 3D 世界的容器,包含所有的物体、光源和相机。
相机相机定义了观察场景的视角,可以是透视相机或正交相机。
渲染器渲染器负责将场景和相机结合起来,生成最终的图像并显示在屏幕上。

Snipaste_2025-02-15_11-18-25.png

场景中物体的组成

Threejs场景中物体被构建的方式多种多样可以通过Geometry 和 Material 组成,可以通过 .glb 等方式从外部引入模型,可以由独特的粒子系统构建,这里我们中的讲述第一个:

在 Threejs中 材质(Material) 与 (Geometry) 几何体通常是分开的,几何体定义了物体的 形状,Material(材质)定义了物体的 表面特性。

假设你要做一个 水瓶:

  • Geometry(形状):你首先需要创建瓶子的 几何形状,比如瓶子本体的 圆柱体,瓶口部分可以用 圆环 来表现。通过几何体,你定义了瓶子的外形,但瓶子的表面细节仍然没有表现出来。

  • Material(材料)

    :接下来,你要给瓶子定义它的材质

    比如:

    • 如果瓶子是 玻璃 材质,材质应该具有透明感、折射光线的效果,并且光泽感强。
    • 如果瓶子里面装的是 水,你可能会给瓶子内部的水部分设置一个 液体材质,使它看起来流动、透明且反射光线。

Snipaste_2025-02-15_14-51-28.png

显示一个平面

接下来,我们将通过代码展示一个简单的平面(Plane)来开始构建 3D 场景。

让 Trae 来吧, Trae 的 Builder 模式是我认为 Trae 最棒的功能,基于Claude-3.5-Sonnet 的 Builder 模式,对于一个新框架的初学者和基建懒人来说一键生成真的很省事!最关键的是:现阶段免费!!!,相比起 cursor 的昂贵,trae 可以说是让我的白嫖之魂燃起来了!!!

Builder 模式下,AI 助手会主动读取当前项目中的文件内容,拆解任务并逐步执行,包括:

  • 提取相关上下文
  • 创建或修改文件
  • 生成并运行终端命令
  • 分析命令运行状态

让我们看看他的表现如何?

traecode1.gif

生成的代码在我看来完全没有问题,其中 main.js主要内容为

import * as THREE from 'three';

// 创建场景
const scene = new THREE.Scene();

// 创建相机
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.PerspectiveCamera(35, aspect, 0.1, 100);
camera.position.z = 3;

// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建平面几何体
const geometry = new THREE.PlaneGeometry(2 * aspect, 2);

// 创建绿色材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide });

// 创建平面网格
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);

// 渲染循环
function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

// 启动动画循环
animate();

// 处理窗口大小变化
window.addEventListener('resize', () => {
    const newAspect = window.innerWidth / window.innerHeight;
    camera.aspect = newAspect;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);

    // 更新平面尺寸以保持比例
    plane.geometry = new THREE.PlaneGeometry(2 * newAspect, 2);
});

得到一个绿色的平面,看起来不错。干得好!Trae

Snipaste_2025-02-15_13-38-57.png

3.纹理概述

什么是纹理?

简单的来说纹理就是给物体表面添加一种“外观”或者“图案” ,就比如我们喜闻乐见的三星广告,(没有黑三星的意思,本人尊重任何手机品牌用户)

纹理覆盖.gif 这里的三星手机屏幕为为手机平面贴上一张草皮的图像,我们肉眼看到的手机就变成了草皮,你可以想象它们可以是各种各样的——光滑的、粗糙的、带有色彩的,甚至是一些很复杂的图案。屏幕上现实的图案就可以看成是一种纹理,它叫图片纹理,可以说是每个 Threejs人员的升级版 Hello World。它们看起来可能是平面的,但通过这些“表面细节”,你会感觉到深度和质感。

简单来说,纹理就是让 3D 物体看起来不那么单调的魔法。更多的纹理我争取在后面的课程一一为您展现。

获取地图纹理!

图像资源获取方式

通常我们的世界地图图像获取方式有很多种,笔者用的最多的分别是

这里我们使用第二个

进入页面后可以随意调节左侧 Panel 的参数,重要的是像素图生成功能 Snipaste_2025-02-15_14-07-21.png

在调试到自己想要的颜色并生成像素图点击下载后,我们得到了用作贴图的纹理图像

chart.png 看起来很不错,那么现在将图片贴上当前平面吧

为平面贴上纹理

很遗憾在Threejs 中我们无法直接使用图像而需要创建一个 TextureClass 的实例才能将图像引入

        // 创建一个平面
        const geometry = new THREE.PlaneGeometry(2 * aspect, 2);
​
        // 加载纹理
        const textureLoader = new THREE.TextureLoader();
        const texture = textureLoader.load('您的图片路径');
        
        // 创建材质并应用纹理
        const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide });

现在我们应该可以在平面上看到纹理了 Snipaste_2025-02-15_14-20-52.png

怎么说呢,感觉跟使用 CSS 没什么区别?这不纯小丑吗?!别急,接下来的内容会稍微难一点,请耐心观看。。。

4.ShaderMaterial (难度飙升)

如果您是第一次接触 Threejs ,就看到这里。其实在内行眼里就约等于 啊?我打宿傩?! ,不过不要紧,会赢的宝贝!

为什么要使用 ShaderMaterial?

此时您可能内心OS:不是哥们,地图已经出现在画布上了,为什么我们要舍近求远,换一种更加复杂的材质来渲染这个平面呢?

答案之一就是:操控性,他可以让页面上的任何一个"像素"发生颜色或者位置的改变进而创造出惊人的效果!

(Tips: 这可能会是下一节课的内容)

什么是 Shader (着色器)?

让我们又又又先看一段视频

让我们来粗略的分析一下这幅画能成功出现在画板上的原因:

  1. 精准定位:每个从管道中发射的彩蛋小球都精准地命中了目标区块。
  2. 精确着色:每个彩蛋小球都精确地装载了相对应的颜色。

在 Threejs 中,着色器也在做着类似的事情。着色器是用 GLSL(OpenGL Shading Language)编写并发送到 GPU 的程序。它们用于定位几何体的每个顶点,并为该几何体的每个可见像素着色。

在这个不断渲染着色的过程中,我们可以向着色器发送大量的数据,例如顶点坐标、网格变换、有关摄像机及其视野的信息、颜色、纹理、灯光、雾等参数。

这时就好比彩蛋发射器被改装为可以连续发射,并在管道内塞入各种有趣颜色的彩蛋小球,来让画布不断发生有趣的变化!

两种类型的 Shader

在 Threejs 中,着色器主要分为两种类型:

  1. 顶点着色器 (Vertex Shader) 顶点着色器的目的是定位几何体的顶点。其基本思想是发送顶点的位置、网格的变换(如位置、旋转和缩放)、相机的信息(如位置、旋转和视野)。然后,GPU 将按照顶点着色器中的指令处理所有这些信息,以便将顶点投影到一个将成为我们渲染的 2D 空间。在使用顶点着色器时,其代码将应用于几何体的每一个顶点。
  2. 片元着色器 (Fragment Shader) 片元着色器(也称为像素着色器)用于处理几何体表面的每个像素的颜色。它接收顶点的颜色、纹理、光照等信息,并计算每个像素的最终颜色。相同的,片段着色器代码也将用于几何体的每个可见片段

那么现在让我们好好理解这两种着色器类型

首先让我们将最开始的材质进行替换

const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide });
const plane = new THREE.Mesh(geometry, material);

替换为以下材质并打开线框模式

const basicMaterial = new THREE.MeshBasicMaterial({ color:'red',wireframe:true });
const plane = new THREE.Mesh(geometry, basicMaterial);

随后在geometry下方加入

console.log(geometry.attributes);
scene.add(new THREE.AxesHelper(2));

让log的内容帮助我们理解几何体顶点这个概念

可以看到我们在geometry的 attribute上获取了三个属性 他们分别是uv ,position,以及 normal。让我们看看这些由Float32BufferAttribute组成的数组携带着这个平面几何体的信息。

itemSize 告诉我们当前数组到底应该按几个为一组进行划分,分割后得到如图所示的内容。

看起来这些内容都存储了顶点的信息,这里我在图中只标出position & uv 值得注意的是UV坐标系原点在WebGL中是左下角(与传统2D坐标系不同) 。

现在将当前的文件扔给Trae,在Chat模式下并要求其将当前 MeshBasicMaterial 替换为 ShaderMaterial 让其生成对应代码片段。

        // 定义着色器
        const vertexShader = `
            void main() {
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `;
​
        const fragmentShader = `
            void main() {
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色 (R,G,B,A)
            }
        `;
​
        const shaderMaterial = new THREE.ShaderMaterial({
            vertexShader: vertexShader,
            fragmentShader: fragmentShader
        });
        const plane = new THREE.Mesh(geometry, shaderMaterial);

看起来像是C语言对吗?现在让我们尽量看懂能理解的部分,看我们能看懂的部分,把其余当做黑盒!先从 vertexShader(顶点着色器)开始:

此时不难发现我们在顶点着色器中又发现了熟悉老朋友,uv 以及 position。看起来他们被传入了 Shader中,真的是这样吗?让我们试试,将 vertexShader

相关内容改为

        const vertexShader = `
            void main() {
                vec3 newPosition = position;
                newPosition.y +=0.1;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
            }
        `;

(Tips: 一般情况下无法更改如 position ,uv等 attribute 他们通常都只是可读,需要有新的容器来接受并更新)

最终得到的结果是整个平面都上升了,这也表示在使用顶点着色器时,其代码将应用于几何体的每一个顶点,即使你有着成千上万的顶点,都只会执行同一段代码。

现在让我们看 fragmentShader 即片段着色器,着重看 main 主函数中究竟做了什么 这里我们将 ShaderMaterial的线框模式去除

        const material = new THREE.ShaderMaterial({
            uniforms: {
                uTexture: { value: texture },
                uPosition: { value: new THREE.Vector3(1,0,0) }
            },
            vertexShader: vertexShader,
            fragmentShader: fragmentShader,
            side: THREE.DoubleSide,
            // wireframe:true 删除此行
        });

目前主函数逻辑主要是

            void main() {
                gl_FragColor = vec4(1.0,0.0,0.0,1.0);
            }

这里由我向您解释

gl_FragColor 是一个四维向量(vec4),分别表示红色(R)、绿色(G)、蓝色(B)和透明度(A)的值。每个分量的取值范围是 0.0 到 1.0。

这也是为什么现在屏幕显示一块红色的平面。

这是fragment Shader最直接的使用方式:使用相同的颜色为所有片段着色。

但我们可以通过片元着色器实现一些动态效果。例如,根据像素的纹理坐标动态改变颜色。

还记得之前解释 geometry中有哪些attribute的图吗?(绿色标识数字为 UV)

我们这里可以使用uv数据进行着色,让我先修改一下 fragmentShader

        // 定义片段着色器
        const fragmentShader = `
            varying vec2 vUv;
            void main() {
                gl_FragColor = vec4(uv.x,uv.y,0.0,1.0);
            }
        `;

为了防止你对uv.x &uv.y产生疑惑,这里附带一定程度的简要说明

向量类型属性数量示例代码说明
vec22vec2 foo = vec2(1.0, 2.0);存储 2D 坐标,具有 x 和 y 属性
foo.x = 1.0; foo.y = 2.0;可以在创建后更改属性
foo *= 2.0;与 float 相乘将同时影响 x 和 y 属性
vec33vec3 bar = vec3(1.0, 2.0, 3.0);存储 3D 坐标,具有 x、y 和 z 属性
purpleColor.r = 0.5;可以使用 r、g、b 来表示颜色
vec3 bar = vec3(foo, 3.0);可部分由 vec2 创建
vec2 bar = foo.xy;可从 vec3 提取部分生成 vec2
vec2 bar = foo.yx;支持 swizzle 操作
vec44vec4 foo = vec4(1.0, 2.0, 3.0, 4.0);存储 4D 坐标,具有 x、y、z 和 w 属性
vec4 bar = vec4(foo.zw, vec2(5.0, 6.0));可组合其他向量

Ctrl+S得到的结果却不尽如人意 Snipaste_2025-02-17_10-41-49.png

让我们用Trae来分析截图中的报错信息,只需要点击图片按钮后上传报错截图

traeBug.png

很快 Trae就将报错原因返回给我们,学习过程中遇到报错原因像我这种懒人也喜欢直接将截图传入 Trae 让他直接分析为何报错!

报错信息告诉我们

  1. 使用了一个名为 uv 的变量,但该变量没有被声明。
  2. 构造函数调用中,提供的数据不足

太奇怪了不是吗? geometry的attribute中明明有 uv,但却在 fragment中不存在

原来在每个顶点之间更改的数据(如它们的位置)称为 attribute,并且只能在 顶点着色器( vertex Shader ) 中使用。

好在 glsl编写过程中,我们可以使用 varying 将数据从顶点着色器发送到片段着色器

        // 定义片段着色器
        const fragmentShader = `
            varying vec2 vUv;
            void main() {
                gl_FragColor = vec4(vUv.x,vUv.y,0.0,1.0);
            }
        `;

此时页面出现了一块多彩的色板

Snipaste_2025-02-17_10-53-18.png

但是 uv,不是只在顶点处存在数据吗?为什么会产生渐变效果呢?

  • 原来**varying 是 GLSL 中用于在顶点着色器和片元着色器之间传递插值数据的变量。它的值会在顶点之间进行插值,然后传递给片元着色器。那么再让我们回看这句话片段着色器代码也将用于几何体的每个可见片段**,每个可见的“像素(在这里简化为像素)”都赋予了自己的颜色。

如果我想传入自己的颜色,而不依托与geometry所拥有的属性呢?

  • glsl也同样提供了一种方式 uniform,他可以帮助你通过传入使用是一些数据,这些数据在所有顶点着色器和片元着色器之间都是不变的。

让我们试试吧!

再次修改 fragment Shader

const fragmentShader = `
    uniform vec3 uColor; // 定义一个 uniform 变量
    varying vec2 vUv;

    void main() {
        gl_FragColor = vec4(uColor, 1.0); // 使用 uniform 颜色
    }
`;

以及 vertex Shader

        const vertexShader = `
            uniform vec3 uPosition;
            varying vec2 vUv;
            void main() {
                vUv = uv;
                vec3 newPosition = position;
                newPosition = (newPosition + uPosition)/2.;//取每个顶点和传入坐标的中点
                gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
            }
        `;

最后通过ShaderMaterial传入这个变量

const material = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
    uniforms: {
        uColor: { value: new THREE.Vector3(0.0, 0.18, 0.65) } , //克莱因蓝
        uPosition: { value: new THREE.Vector3(1,0,0) }
    }
});

得到了

Snipaste_2025-02-17_11-05-57.png

应用 ShaderMaterial

当然目前我们已经掌握了 glsl 的一些基础知识,度过了所谓的新手教程!接下来让我们试试如何将他们运用到画布上吧。

之前我们在MeshBasicMaterial中实现了应用地图贴图的操作,但是 ShaderMaterial 该如何使用呢?

让我们问问Trae 吧!

我们现在需要让之前获取的图片纹理显示在 套用ShaderMaterial 的 PlaneGeometry上,再次让我们使用 Trae的 Chat 模式吧!不过不是通过侧边的对话窗口,而是直接在代码片段中唤起对话栏

Chat模式能够在编码的过程中随时唤起内嵌对话,这种工作方式非常的方便,不用频繁地在IDE 与 一些AI Agent之间来回切换。不会打断思路,也节约很多时间。

tradcode2.gif

让我们来看这段成功运行的代码

// 加载纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('./MapChart.png');

// 定义着色器
const vertexShader = `
    varying vec2 vUv;
    void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
`;

const fragmentShader = `
    uniform sampler2D uTexture;
    varying vec2 vUv;
    void main() {
        gl_FragColor = texture2D(uTexture, vUv);
    }
`;

const shaderMaterial = new THREE.ShaderMaterial({
    uniforms: {
        uTexture: { value: texture }
    },
    vertexShader: vertexShader,
    fragmentShader: fragmentShader
});

首先 uniform在我们之前的介绍中提到他可以接收外部传递的数据并共享给 vertexShader & fragmentShader。其次我们知道varying 是 GLSL 中用于在顶点着色器和片元着色器之间传递插值数据的变量。它的值会在顶点之间进行插值,然后传递给片元着色器。

这就好比一张图片被一个极为精密的坐标轴分割成无数细块,每个“像素细块”都有一个坐标组成比如左下角的(0,0),在被UV映射后他就会贴在几何体相应uv的位置上,只不过目前是PlaneGeomtry ,uv坐标为(0,0) 刚好在左下角。

Snipaste_2025-02-17_16-05-56.png

(假装有极为精密的坐标)

随后我们让平面几何体向屏幕内偏倒,这里我们再次使用Tres的Chat Mode

traeCode3.gif

这个时候已经初具雏形了不是吗?

现在我们只剩两步就可实现这个页面:

  1. 让地图随着时间推移向右侧移动
  2. 给背景一个渐变的过程,沿着平面底部中央向外扩散出一个渐变的可见光圈

推动吧!地图!

在前面解释了uv坐标与图片上“像素细块”的对应关系,那么再进一步,是不是我们操作uv坐标就能操控画布上的图片呢?

Talk is cheap, show me the code! Trae!!!

traeCode4.gif

在 Trae 提供的代码片段中,此时的fragmentShader变为

const fragmentShader = `
    uniform sampler2D uTexture;
    uniform float uTime;
    varying vec2 vUv;
    void main() {
        vec2 scrolledUv = vec2(vUv.x + uTime, vUv.y);
        gl_FragColor = texture2D(uTexture, scrolledUv);
    }
`;

除了通过uniform 传入 uTime这个操作外,并对uv进行操作外。还有一个最关键的操作就是

// 渲染循环
function animate() {
    requestAnimationFrame(animate);

    // 更新时间uniform,这里的0.0005控制滚动速度,可以根据需要调整
    shaderMaterial.uniforms.uTime.value -= 0.0005;

    renderer.render(scene, camera);
}

在渲染循环中加入 uTime的更新逻辑,requestAnimationFrame 的作用是请求浏览器在下一次重绘之前调用用户提供的回调函数。也就是说里面的逻辑会不断被调用 。uTime的变化会使得 uv 不断减小。比如点 (1,0) ,在下一个 tick 会变为 (0.0095,X)。 也意味着下一个 tick ,图片内容会向右侧移动。

02.gif

哇!出现了新鲜的BUG。这是因为**在 Three.js 中当纹理坐标超出 [0, 1] 的范围时,默认情况下,纹理会使用边缘像素的颜色填充这些区域。**这也是为什么会存在拖尾~。

解决方案也非常简单,只需要在加载纹理下方添加 texture.wrapS = THREE.RepeatWrapping表明我需要让纹理在水平轴上重复即可。

// 加载纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('./MapChart.png');
texture.wrapS = THREE.RepeatWrapping;

这里补充一些知识:

在 Three.js 中,texture.wrapS 和 texture.wrapT 是用于控制纹理在 S 轴(水平)和 T 轴(垂直)方向上的重复方式的属性。它们的主要区别在于它们分别影响纹理的哪个方向。

  • 常用值:
    • THREE.RepeatWrapping:纹理在 S 轴上重复。
    • THREE.ClampToEdgeWrapping:纹理在 S 轴上不重复,使用边缘颜色填充超出范围的区域。
    • THREE.MirroredRepeatWrapping:纹理在 S 轴上重复,但每次重复时镜像翻转。

好了,现在需求一已经完成了。

02.gif

渐变吧!地图 !

此时只需要在实现距离屏幕越远越透明这一个效果即可

之前我们了解到 传递到fragmentShader的vUv 会在 X 轴 & Y 轴自动进行差值。也就是说此时平面上 vUv.y 是沿着平面所处的Y轴越来越大的(范围为0-1)。

并且你还记得 gl_FragColor 是一个四维向量(vec4),分别表示红色(R)、绿色(G)、蓝色(B)和透明度(A)的值。

而我们想到实现效果是需要透明度沿着 Y轴越来越小,即由 1 渐变到 0 。

那么我们可以向Trae提问!

嘿 Trae,帮助我通过vUv.y操控画布透明度,从1-0

traeCode5.jpg

给出的结果和我们一开始预期的一样

// ... existing code ...

const fragmentShader = `
    uniform sampler2D uTexture;
    uniform float uTime;
    varying vec2 vUv;
    void main() {
        vec2 scrolledUv = vec2(vUv.x + uTime, vUv.y);
        vec4 texColor = texture2D(uTexture, scrolledUv);
        float alpha = 1.0 - vUv.y; // 从底部(1)到顶部(0)的透明度
        gl_FragColor = vec4(texColor.rgb, texColor.a * alpha);
    }
`;

const shaderMaterial = new THREE.ShaderMaterial({
    uniforms: {
        uTexture: { value: texture },
        uTime: { value: 0 }
    },
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    transparent: true  // 启用透明度
});

// ... existing code ...

但还是让我们看看Trae帮我们干了什么吧!

首先是将 vUv.y的值进行翻转,然后作为透明度权重之一赋值给 gl_FragColor 的 透明度

        float alpha = 1.0 - vUv.y; // 从底部(1)到顶部(0)的透明度
        gl_FragColor = vec4(texColor.rgb, texColor.a * alpha);

其次就是当我们在使用关于透明的逻辑时,通常需要且仅需打开ShaderMaterial的transparent: true

    transparent: true  // 启用透明度

最后得到的效果是

Snipaste_2025-02-18_09-15-53.jpg

5.来点有趣的 UI 怎么样

让AI敲定页面布局和复刻页面元素,这是我最喜欢使用 AI IDE的方式! 让我们看看 Trae 的表现怎么样吧~

先让我们将页面原型上传到 Trae 的对话栏中,值得注意的是Trae 可以通过 # 符号添加多种类的上下文,包括代码、文件、文件夹和工作区,对我来说,我时常使用这个来引用一整个文件,这样显示出来的对话结构清晰明了!

让我们通过#File指定整个index.html文件

img

随后通过 图片上传功能将事先准备好的原型图传入对话模型,不得不说结合Claude这样视觉模型的多模态输入 IDE 用起来真的是太爽啦!

21e0fee89ceb06b9b468a3680e1746e2.png

在有 Claude 这个专精编码的视觉模型助力下,Trae在这次的表现真的让我叹为观止!

traeCode.gif

他在短短20秒内生成了一个看起来非常完善的布局。tailwindcss风格,并且整体布局有模有样

Snipaste_2025-02-18_10-02-59.jpg

看起来只需要将canvas脱离文档流然后找个好看的字体就行了!

让我们上 Google Font 上看看吧!

最后集合下载完资源再找到刚刚的 Chat 对话栏!在刚刚发出的对话旁点击撤销更改!

back.jpg

代码回退后再次引入字体,最后生成页面样式!然后手动微调一下界面!最终成品就出来了!!!

final.jpg

源码地址 : github.com/hexianWeb/e…

6.总结

通过本教程,我们共同完成了:

  1. Three.js核心机制掌握
  • 场景三要素(场景/相机/渲染器)的协同原理
  • 几何体与材质的黄金组合应用
  • UV映射机制与纹理的动态控制
  1. Shader魔法解析
  • 顶点着色器:几何形态的终极掌控者
  • 片段着色器:像素级渲染的艺术画笔
  • Uniform/Varying的数据传递哲学
  1. 动态视效实现
  • 时间驱动的无缝滚动纹理
  • 基于UV坐标系的渐变蒙版
  • 三维空间与二维UI的完美融合

在此过程中Trae也帮到了我们不少!

  • Builder 模式帮助我们快速快速从零到一构建项目的起始框架!
  • 流畅的Chat模式以及补全,帮助我们一步一步学习这个框架的基本使用,详细的代码注释&讲解 让仅我们在一个 IDE的侧边对话栏内就可以丝滑流畅进行下去, 而不用在浏览器和IDE间频繁互动。
  • 多模态输入,不仅通过上传报错信息截图,让 AI 助手协助解决错误,提升问题诊断效率。还能借助Claude这样强大的视觉编码模型仅通过设计稿就能够快速复刻出较高保真原型,让原型开发周期缩短至传统模式的1/5。

7.鸽子大王的最后一些话

距离上次更新文章已经过去半年了,我才为这个 Hero Section 专栏新添一篇文章。老实说,我不是一个分享欲很强的人,好好的一个专栏也鸽了有半年之久。有些认识我的群友应该都见识过我留图不留码的习惯。

微信圖片_20250218140218.jpg

在过去的一年里,我搞过 Threejs 逆向,也见识过很多有趣的案例。虽然目前我的技术并没有达到业界大牛的水准,但我还是想开始系统地分享一些东西(起码从年更变成月更),包括如何真正系统地学习 Threejs、一些有趣的 Web 3D 设计案例、掘金上哪些厉害的 Threejs 玩家以及文章。我深信 Web3D 将会成为未来前端设计与发展的趋势之一,这也是我想要创建整个专栏的初衷。

关于未来的文章方向

在接下来的文章中,我将不再过多地介绍 Threejs 和 Shader 的基础知识 (非常抱歉,但这可能是今年最后一篇入门级教程),而是更多地聚焦于中高级应用和实战技巧。如果您对 Threejs 和 Shader 的基本使用已经有一定了解,那么接下来的内容将会更加适合您。我们将深入探讨如何利用 Shader 实现复杂的光照、阴影、粒子效果,以及如何将 Threejs 应用到实际项目中,打造引人注目的 Hero Section。

加入社区,共同成长

如果您对 Threejs 这个 3D 图像框架很感兴趣,或者您也深信未来国内会涌现越来越多 3D 设计风格的网站,欢迎加入 ice 图形学社区。这里是国内 Web 图形学最全的知识库,致力于打造一个全新的图形学生态体系!您可以在认证达人里找到我这个 Threejs 爱好者和其他大佬。

此外,如果您很喜欢 Threejs 又在烦恼其原生开发的繁琐,那么我诚邀您尝试 Tresjs 和 TvTjs, 他们都是基于 Vue 的 Threejs 框架。 TvTjs 也为您提供了大量的可使用案例,并且拥有较为活跃的开发社区,在这里你能碰到志同道合的朋友一起做开源!

推荐框架:Tresjs 和 TvTjs

此外,如果您很喜欢 Threejs 又在烦恼其原生开发的繁琐,那么我诚邀您尝试 Tresjs 和 TvTjs。它们都是基于 Vue 的 Threejs 框架,提供了大量的可使用案例,并且拥有较为活跃的开发社区。在这里,您能碰到志同道合的朋友一起做开源!

我目前参与过 Tresjs 和 TvTjs 的开源项目,虽然这些项目目前无法与 R3F 这样社区活跃的开源框架相比,但对于集成一个 3D 业务场景或开发一个好看的 Vue + Web3D 页面,它们的开发体验真的能称得上丝滑!当前项目也被集成到了 TvTjs 预览 中。

TvTjs.gif

最后

感谢您阅读到这里,希望这篇文章能为您带来一些启发和帮助。如果您有任何问题或建议,欢迎在评论区留言,或者加入 ice 图形学社区 与我 (点击右上角认证达人) 交流。接下来的文章会更加深入和实用,期待与您一起探索 Threejs 的无限可能!

让我们在 Web3D 的世界里,继续前行!