Three.js着色器编程进阶:从入门到实战指南

2026-03-27 0 236

Three.js着色器编程进阶:从入门到实战指南

核心结论:着色器编程是Three.js视觉效果的终极控制方式

着色器()是运行在GPU上的小程序,允许你直接控制每一个像素和顶点的渲染结果。对于Three.js开发者而言,掌握着色器编程意味着:

完全控制渲染管线:突破等内置材质的限制

实现极致性能:复杂视觉效果通过GPU并行计算,CPU负载极低

创造独特视觉风格:水流、火焰、光效、扭曲等效果均可通过着色器实现

本文档提供从基础概念到实战应用的全链路指南,所有代码示例基于Three.js r128及以上版本,GLSL版本为WebGL 1.0(GLSL ES 1.00)。

第一部分:着色器基础概念(必须掌握)

1.1 顶点着色器( )与片元着色器( )

类型 执行阶段 核心职责 必须输出的变量
顶点着色器 每个顶点执行一次 计算顶点最终位置,传递数据到片元着色器 (vec4)
片元着色器 每个像素(片元)执行一次 计算像素最终颜色 (vec4)

1.2 GLSL基础数据类型

// 标量类型
float  a = 1.0;      // 浮点数(必须带小数点)
int    b = 1;        // 整数
bool   c = true;     // 布尔值
// 向量类型(最常用)
vec2  v2 = vec2(0.5, 0.8);   // 2分量浮点向量,可用xyzw/rgba访问
vec3  v3 = vec3(1.0, 0.0, 0.0);
vec4  v4 = vec4(1.0);         // 所有分量为1.0
// 矩阵类型
mat3  m3 = mat3(1.0);         // 3x3单位矩阵
mat4  m4 = mat4(1.0);         // 4x4单位矩阵

1.3 顶点着色器标准结构

// 默认接收的数据(Three.js自动传递)
 mat4 ;      // 模型视图矩阵
 mat4 ;     // 投影矩阵
 vec3 ;           // 顶点位置
 vec2 uv;                 // UV坐标
// 输出到片元着色器的变量(需使用)
 vec2 vUv;
void main() {
    vUv = uv;  // 传递UV坐标
    // 计算最终顶点位置(标准写法)
     = 1.0;  // 可选:点精灵大小
     =     vec4(, 1.0);
}

1.4 片元着色器标准结构

 highp float;  // 精度限定符:lowp//highp
 vec2 vUv;       // 接收顶点着色器传来的数据
void main() {
    // 输出颜色(RGB+Alpha)
     = vec4(vUv.x, vUv.y, 0.5, 1.0);
}

第二部分:Three.js中编写着色器的两种方式

2.1 (推荐新手)

  as THREE from 'three';
const  = new THREE.(2, 2);
const  = new THREE.({
    : {
        time: { value: 0 },
        : { value: new THREE.Color() },
        : { value: new THREE.Color() }
    },
    : 
         vec2 vUv;
        void main() {
            vUv = uv;
             = 1.0;
             =  </>  <> vec4(, 1.0);
        }
    ,
    : 
         float time;
         vec3 ;
         vec3 ;
         vec2 vUv;
        void main() {
            vec3 color = mix(, , vUv.x + sin(vUv.y </> 10.0 + time) <> 0.1);
             = vec4(color, 1.0);
        }
    ,
    side: THREE.  // 可选:双面渲染
});
const mesh = new THREE.Mesh(, );
scene.add(mesh);
// 动画循环中更新
 (time) {
    ..time.value = time  0.001;
    e();
}

特性

Three.js自动提供顶点着色器中的默认(, uv, 等)

自动提供和

支持透明、深度测试等标准材质属性

2.2 (完全控制)

const  = new THREE.({
    : {
        time: { value: 0 },
        : { value: . },
        : { value: mesh. },
        // 需要手动声明所有
    },
    : 
         mat4 ;
         mat4 ;
         vec3 ;
         vec2 uv;
         vec2 vUv;
        void main() {
            vUv = uv;
             =  <>  </> vec4(, 1.0);
        }
    ,
    : ...
});

特性

Three.js不注入任何默认和

必须在中显式声明所有使用的矩阵和属性

适用于需要精确控制或避免Three.js自动注入的场景

第三部分:进阶技巧与常用模式

3.1 UV坐标变换与重复纹理

 vec2 vUv;
void main() {
    // 缩放UV(重复次数)
    vec2  = vUv  3.0;
    // 偏移UV(滚动效果)
    vec2  = vUv + vec2(time  0.1, 0.0);
    // 旋转UV
    float angle = time  0.5;
    vec2  = vUv - 0.5;
    vec2  = vec2(
        .x  cos(angle) - .y  sin(angle),
        .x  sin(angle) + .y  cos(angle)
    ) + 0.5;
    // 使用fract实现无缝平铺
    vec2  = fract(vUv  5.0);
     = vec4(.x, .y, 0.5, 1.0);
}

3.2 常用数学函数

// 平滑插值(三次方)
float (float edge0, float edge1, float x);
// 正弦/余弦(参数为弧度)
float  = sin(angle);
float  = cos(angle);
// 取模(返回余数)
float  = mod(x, y);
// 限制范围
float  = clamp(x, 0.0, 1.0);
// 线性插值
float  = mix(a, b, t);
// 绝对值
float  = abs(x);
// 幂运算
float  = pow(x, 2.0);
// 平方根
float  = sqrt(x);

3.3 噪声函数(模拟自然效果)

GLSL标准库不包含噪声函数,需要手动实现。以下是噪声的实用实现:

// 简单的随机函数
float (vec2 st) {
     fract(sin(dot(st.xy, vec2(12.9898,78.233)))  43758.);
}
// 平滑插值噪声
float noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    float a = (i);
    float b = (i + vec2(1.0, 0.0));
    float c = (i + vec2(0.0, 1.0));
    float d = (i + vec2(1.0, 1.0));
    vec2 u = f  f  (3.0 - 2.0  f);
     mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
// FBM(分形布朗运动)叠加多层噪声
float fbm(vec2 st, int , float , float ) {
    float value = 0.0;
    float  = 0.5;
    float  = 4.0;
    for(int i = 0; i < ; i++) {
        value +=   noise(st  );
         = ;
         = ;
    }
     value;
}
// 使用示例:生成云彩效果
void main() {
    vec2 st = vUv  5.0;
    float  = fbm(st, 5, 0.5, 2.0);
    vec3 color = mix(vec3(0.2, 0.4, 0.8), vec3(0.9, 0.9, 0.9), );
     = vec4(color, 1.0);
}

3.4 光照计算(在着色器中实现自定义光照)

// 顶点着色器
 mat4 ;
 mat4 ;
 mat3 ;  // Three.js自动提供
 vec3 ;
 vec3 ;
 vec3 ;
 vec3 ;
void main() {
     = (  );
    vec4  =   vec4(, 1.0);
     = -.xyz;
     =   ;
}
// 片元着色器
 vec3 ;
 vec3 ;
 vec3 ;
 vec3 ;
 vec3 ;
void main() {
    vec3  = ();
    vec3  = ();
    // 漫反射
    float diff = max(dot(, ), 0.0);
    vec3  = diff  ;
    // 环境光
    vec3  = ;
    // 高光(Blinn-Phong)
    vec3  = ();
    vec3  = ( + );
    float spec = pow(max(dot(, ), 0.0), 32.0);
    vec3  = spec  vec3(1.0);
    vec3 color = ( +  + );
     = vec4(color, 1.0);
}

第四部分:性能优化与调试

4.1 性能优化核心准则

优化项 说明 优先级
减少分支语句 避免在中使用if/else,使用step/mix等函数替代
精度限定符 不要求高精度时使用或lowp
纹理采样次数 尽量减少调用
统一变量数量 减少数量,必要时打包为vec4数组
顶点着色器复杂度 保持顶点着色器简单,将计算移到片元着色器

分支优化示例

// 不推荐
if (value > 0.5) {
    color = vec3(1.0, 0.0, 0.0);
} else {
    color = vec3(0.0, 0.0, 1.0);
}
// 推荐
color = mix(vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), step(0.5, value));

4.2 调试着色器的三种方法

颜色可视化输出

// 调试UV坐标
 = vec4(vUv.x, vUv.y, 0.0, 1.0);
// 调试法线
 = vec4(  0.5 + 0.5, 1.0);
// 调试时间
 = vec4(sin(time), cos(time), 0.0, 1.0);

使用浏览器扩展

Three.js着色器编程进阶

.js:捕获WebGL帧,查看源码和值

Three.js :实时查看材质状态

错误检查

const  = new THREE.({ ... });
.('error', (event) => {
    .error('  error:', event.error);
});

4.3 常见错误及解决方案

错误现象 常见原因 解决方案
物体不显示(黑色) 片元着色器未输出有效颜色;顶点位置超出裁剪空间 检查赋值;验证的w分量
编译错误提示 GLSL语法错误;使用了未声明的变量;精度未声明 检查分号、括号匹配;验证/声明
纹理显示异常 UV坐标传递错误;纹理未加载完成 输出vUv验证;确保纹理ready后设置
性能急剧下降 片元着色器过于复杂;过多次纹理采样 简化数学运算;减少采样次数

第五部分:实战案例 – 动态流光效果

5.1 完整代码实现

<! html>
<html lang="zh-CN">
<head>
    <meta ="UTF-8">
    <style>
        body { : 0; : ; }
        #info {
            : ;
            top: 20px;
            left: 20px;
            color: white;
            : rgba(0,0,0,0.6);
            : 10px;
            -: 5px;
            font-: ;
            -: none;
            z-index: 100;
        }
    </style>
</head>
<body>
    <div id="info">
        Three.js 着色器流光效果 | 鼠标移动控制方向
    </div>
    < type="">
        {
            "": {
                "three": "@0.128.0/build/three..js"
            }
        }
    </>
    < type="">
          as THREE from 'three';
         {  } from '@0.128.0//jsm//.js';
        // 初始化场景、相机、渲染器
        const scene = new THREE.Scene();
        scene. = new THREE.Color();
        const  = new THREE.(45, . / ., 0.1, 1000);
        ..set(2, 1.5, 3);
        .(0, 0, 0);
        const  = new THREE.({ : true });
        .(., .);
        .(.);
        .body.(.);
        // 轨道控制
        const  = new (, .);
        . = true;
        // 创建几何体(环面结)
        const  = new THREE.(0.8, 0.2, 200, 32, 3, 4);
        // 着色器
        const  = {
            uTime: { value: 0 },
            : { value: new THREE.(0.5, 0.5) },
            : { value: new THREE.Color() },
            : { value: new THREE.Color() },
            : { value: 0.8 }
        };
        // 顶点着色器
        const  = 
             vec2 vUv;
             vec3 ;
            void main() {
                vUv = uv;
                vec4  =  <> vec4(, 1.0);
                 = .xyz;
                 = 1.0;
                 =  </> ;
            }
        ;
        // 片元着色器
        const  = 
             float uTime;
             vec2 ;
             vec3 ;
             vec3 ;
             float ;
             vec2 vUv;
             vec3 ;
            // 随机函数
            float (vec2 st) {
                 fract(sin(dot(st.xy, vec2(12.9898,78.233))) <> 43758.);
            }
            // 平滑噪声
            float noise(vec2 st) {
                vec2 i = floor(st);
                vec2 f = fract(st);
                float a = (i);
                float b = (i + vec2(1.0, 0.0));
                float c = (i + vec2(0.0, 1.0));
                float d = (i + vec2(1.0, 1.0));
                vec2 u = f </> f <> (3.0 - 2.0 </> f);
                 mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
            }
            void main() {
                // 基础UV变换
                vec2 uv = vUv;
                // 动态流动方向(受鼠标影响)
                vec2  = vec2(0.8, 0.3) + ( - 0.5) <> 1.5;
                float  = uTime </> 0.8;
                // 第一层流动条纹
                float  = sin(uv.x <> 12.0 - uv.y </> 8.0 +  <> 2.0);
                 = clamp( </> 0.8 + 0.5, 0.0, 1.0);
                // 第二层流动(反向)
                float  = cos(uv.y <> 15.0 + uv.x </> 5.0 -  <> 1.5);
                 = clamp( </> 0.7 + 0.6, 0.0, 1.0);
                // 噪声纹理增强细节
                vec2  = uv <> 8.0 + uTime </> 0.2;
                float  = noise();
                // 组合流光强度
                float  = ( + ) <> 0.8 +  </> 0.3;
                 = pow(, 1.2) <> ;
                // 根据UV和流动方向混合颜色
                vec3  =  + vec3(sin(uTime </> 0.5) <> 0.2, cos(uTime </> 0.7) <> 0.2, sin(uTime </> 0.9) <> 0.2);
                vec3  =  + vec3(cos(uTime </> 0.6) <> 0.2, sin(uTime </> 0.8) <> 0.2, cos(uTime </> 1.0) <> 0.2);
                vec3  = mix(, , uv.x + uv.y </> 0.5);
                vec3  = vec3(0.8, 0.4, 1.0);
                // 最终颜色:基础色 + 流光色
                vec3  =  +  <>  </> 0.8;
                // 添加边缘光晕(基于位置深度)
                float  = 1.0 - abs(.z) <> 0.8;
                 += vec3(0.5, 0.3, 0.8) </>  <> 0.3;
                // 输出
                 = vec4(, 1.0);
            }
        ;
        const  = new THREE.({
            : ,
            : ,
            : ,
            side: THREE.,
            : false,
            : true  // 启用色调映射,增强视觉效果
        });
        const mesh = new THREE.Mesh(, );
        scene.add(mesh);
        // 添加简单网格辅助线(可选)
        const  = new THREE.(5, 20, , );
        ..y = -1;
        scene.add();
        // 鼠标交互
        const mouse = new THREE.(0.5, 0.5);
        .('', (event) => {
            mouse.x = event. / .;
            mouse.y = event. / .;
            ..value = mouse;
        });
        // 动画循环
        let clock = new THREE.Clock();
         () {
            const  = clock.();
            .uTime.value = ;
            // 自动旋转效果(可选)
            // mesh..y =   0.2;
            // mesh..x = Math.sin(  0.3)  0.3;
            .();
            .(scene, );
            e();
        }
        ();
        // 窗口适配
        .('', , false);
         () {
            . = . / .;
            .ix();
            .(., .);
        }
        .log('着色器流光效果已启动');
    </>
</body>
</html>

5.2 效果说明与参数调节

参数 作用 推荐调节范围
uTime 动画时间,控制流动速度 自动更新
鼠标位置,影响流动方向 0-1范围
起始颜色 RGB任意
结束颜色 RGB任意
流光强度 0.0-1.5

第六部分:进阶学习路径与资源

6.1 学习路径建议

1. 基础阶段(1-2周):掌握GLSL语法、顶点/片元着色器职责、Three.js中的使用

2. 进阶阶段(3-4周):实现UV变换、噪声函数、基础光照、纹理采样

3. 高级阶段(5-8周):后处理效果()、自定义几何体数据传递、性能优化

4. 精通阶段:实现完整渲染管线定制、 (WebGL 2.0)

6.2 权威资源列表

资源类型 名称 说明
官方文档 The Book of 最权威的着色器入门教程,中英文版本
官方规范 GLSL ES 1.00 Group发布的GLSL ES标准文档
Three.js示例 Three.js 官方着色器示例集合
社区参考 全球最大的着色器作品库,可直接参考算法实现
调试工具 .js WebGL帧调试器

6.3 常见问题FAQs

Q1:如何将外部纹理传递到着色器?

: {
    : { value: new THREE.().load('.jpg') }
}
  ;
 vec2 vUv;
void main() {
    vec4  = (, vUv);
     = ;
}

Q2:顶点着色器和片元着色器中的精度声明有何区别?

顶点着色器通常使用 highp float;

片元着色器中,移动端建议使用 float;以获得更好性能

纹理坐标建议使用 float;

Q3:如何让着色器支持透明?

. = true;
 = vec4(color, alpha);  // alpha < 1.0

Q4:着色器中的更新频率有限制吗?

每帧更新均有效,但注意性能

大量建议打包为数组或纹理传递

着色器编程是Three.js进阶的核心技能,掌握了着色器即掌握了WebGL渲染的最后一块拼图。本文涵盖了从概念到实战的完整知识体系,所有代码示例均经过验证可直接运行。建议读者按照学习路径逐步实践,利用等平台进行算法实验,逐步构建自己的着色器知识库。如有疑问,可参考官方文档或社区资源获取最新信息。

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

七爪网 行业资讯 Three.js着色器编程进阶:从入门到实战指南 https://www.7claw.com/2827077.html

七爪网源码交易平台

相关文章