光影编程师:Threejs Shader 基础全攻略

介绍

Shader 是一种运行在 GPU 上的小程序,用于实现一些图形效果。在 Three.js 中,我们可以使用 GLSL ES 语言来编写 Shader,并将其应用到材质上,从而实现各种图形效果。

可以做什么

  • 实现各种图形效果,如光照、阴影、反射、折射等。

  • 实现自定义的材质,如金属、塑料、玻璃等。

  • 实现粒子效果,如烟雾、火焰、水波等。

  • 实现动态纹理,如动态纹理、动态贴图等。

GLSL ES

GLSL ES 是一种用于编写 Shader 的编程语言,它是一种基于 C 语言的编程语言,具有 C 语言的大部分语法和特性。在 GLSL ES 中,我们可以使用一些特殊的语法和函数来实现各种图形效果。

基础语法

  • 变量声明:在 GLSL ES 中,我们可以使用 floatintbool 等数据类型来声明变量,例如:
glsl 复制代码
float x = 1.0;
int y = 2;
bool z = true;
  • 函数声明:在 GLSL ES 中,我们可以使用 voidfloatint 等数据类型来声明函数,例如:
glsl 复制代码
void myFunction(float a, int b) {
  // 函数体
}
float myFunction2(int a) {
  // 函数体
  return 1.0;
}
  • 循环和条件语句:在 GLSL ES 中,我们可以使用 forwhileif 等循环和条件语句,例如:
glsl 复制代码
for (int i = 0; i < 10; i++) {
  // 循环体
}
if (x > 0.5) {
  // 条件体
}

向量

GLSL ES 中,向量可以标时多种数据,也能进行多种数学运算。

例如,我们可以使用 vec3 来表示颜色,其中 vec3 是一个包含三个浮点数的向量,分别表示红、绿、蓝三个颜色通道的值。例如:

glsl 复制代码
vec3 color = vec3(1.0, 0.0, 0.0); // 红色

我们也可以使用 vec3 来表示位置、方向、法线等,例如:

glsl 复制代码
vec3 position = vec3(0.0, 0.0, 0.0); // 位置
vec3 direction = vec3(0.0, 0.0, 1.0); // 方向
vec3 normal = vec3(0.0, 1.0, 0.0); // 法线

常用关键字:

  • vec2: 二维向量,分量是浮点数。

  • vec3: 三维向量,分量是浮点数。

  • vec4: 四维向量,分量是浮点数。

  • ivec2: 二维向量,分量是整数。

  • ivec3: 三维向量,分量是整数。

  • ivec4: 四维向量,分量是整数。

  • bvec2: 二维向量,分量是布尔值。

  • bvec3: 三维向量,分量是布尔值。

  • bvec4: 四维向量,分量是布尔值。

  • mat2: 二维矩阵,包含两个二维向量。

  • mat3: 三维矩阵,包含三个三维向量。

  • mat4: 四维矩阵,包含四个四维向量。

着色器材质

Three.js 中,我们可以使用 ShaderMaterial 来创建一个着色器材质,并将其应用到几何体上,从而实现各种图形效果。

使用 ShaderMaterial

之前介绍了很多材质,例如:创建一个平面,使用基础网格材质 MeshBasicMaterial

js 复制代码
const geometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.MeshBasicMaterial({
  color: 0xff0000,
});
const mesh = new THREE.Mesh(geometry, material);

现在,我们使用 ShaderMaterial 来创建一个材质,并应用到平面上。

js 复制代码
const geometry = new THREE.PlaneGeometry(100, 50);
const material = new THREE.ShaderMaterial({
  vertexShader: "...", // 顶点着色器
  fragmentShader: "...", // 片元着色器
});
const mesh = new THREE.Mesh(geometry, material);

设置顶点着色器

ShaderMaterial 顶点着色器属性 vertexShader,是一个字符串,表示顶点着色器的代码。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: "", // 顶点着色器
});

设置顶点着色器主函数

根据 GLSL ES 的语法,顶点着色器需要有一个主函数 main(),函数无返回值,前面加上 void 关键字。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `
    void main() {
      // 顶点着色器代码
    }
  `,
});

内置变量

GLSL ES 中,我们可以使用 attributeuniformvarying 等关键字来声明变量,这些变量可以在顶点着色器和片元着色器之间传递。

  • gl_Position

gl_Position 是顶点着色器的一个内置变量,表示顶点的位置。在 GLSL ES 中,顶点着色器需要输出一个 vec4 类型的变量 gl_Position,表示顶点的位置。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `
    //注意在主函数外面声明,默认提供,不用自己写
    attribute vec3 position; 
    void main() {
      gl_Position = vec4(position, 1.0);
    }
  `,
});

attribute: 顶点属性,只能在顶点着色器中使用,用于传递几何体的顶点数据,例如顶点的位置、法线、颜色等。

使用 ShaderMaterial 的时候,Threejs 会在内部把内置变量 position 与几何体的顶点位置数据 geometry.attributes.position 进行绑定,这就意味着,在顶点着色器代码中访问 position 变量,就相当于访问几何体的顶点位置数据。

  • modelMatrix

modelMatrix 是顶点着色器的一个内置变量,表示模型的模型矩阵。在 GLSL ES 中,顶点着色器可以访问 mesh.matrixWorld 变量,用于计算顶点的位置。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `
    uniform mat4 modelMatrix;
    void main() {
      gl_Position = modelMatrix * vec4(position, 1.0);
    }
  `,
});

使用 ShaderMaterial 的时候,Threejs 会自动获取模型世界矩阵 mesh.matrixWorld 的值,赋值给变量 modelMatrix。这意味着,模型矩阵 modelMatrix 包含了模型自身的位置、缩放、姿态角度信息。

uniform: 全局变量,可以在顶点着色器和片元着色器中使用,用于传递全局数据,例如光源位置、颜色等。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `
    uniform float opacity;//uniform声明透明度变量opacity
    void main() {
     gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `,
});

uniform 变量传值,在 Three.js 中,我们可以在创建 ShaderMaterial 的时候,通过 uniforms 属性来传递。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `
    uniform float opacity;
    void main() {
      gl_Position = vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, opacity);
    }
  `,
  uniforms: {
    opacity: { value: 1.0 }, // 传递透明度变量opacity的值
  },
});
  • viewMatrix

viewMatrix 是顶点着色器的一个内置变量,表示视图矩阵。在 GLSL ES 中,顶点着色器可以访问 camera.matrixWorldInverse 变量,用于计算顶点的位置。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `
    uniform mat4 viewMatrix;
    void main() {
      gl_Position = viewMatrix * modelMatrix * vec4(position, 1.0);
    }
  `,
});
  • projectionMatrix

projectionMatrix 是顶点着色器的一个内置变量,表示投影矩阵。在 GLSL ES 中,顶点着色器可以访问 camera.projectionMatrix 变量,用于计算顶点的位置。

js 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `
    uniform mat4 projectionMatrix;
    void main() {
      gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
    }
  `,
});

设置片元着色器

ShaderMaterial 片元着色器属性 fragmentShader,是一个字符串,表示片元着色器的代码。

js 复制代码
const material = new THREE.ShaderMaterial({
  fragmentShader: "", // 片元着色器
});
  • gl_FragColor

gl_FragColor 是片元着色器的一个内置变量,表示片元的颜色。在 GLSL ES 中,片元着色器需要输出一个 vec4 类型的变量 gl_FragColor,表示片元的颜色。

js 复制代码
const material = new THREE.ShaderMaterial({
  fragmentShader: `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
    }
  `,
});

效果

js 复制代码
//创建平面
const geometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.ShaderMaterial({
  vertexShader: `
    void main() {
        // 投影矩阵 * 视图矩阵 * 模型矩阵 * 顶点位置,如果不乘模型矩阵,则不会有模型变换
        gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    void main() {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `,
});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);

modelViewMatrix :视图矩阵 * 模型矩阵

js 复制代码
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);

注意: 每个语句后面要加分号,否则会报错。

Threejs基础系列课程差不多临近结尾了,大家还有什么想学的可以留言。接下来会准备一些项目实战教程,大家可以学到Threejs的应用,在做案例的过程中对之前的基础知识做补充,敬请期待吧!!!

书洞笔记

相关推荐
凯哥1970几秒前
如何将你写的 js 模块发布到 npmjs 给大家使用
前端
章若楠圈外男友5 分钟前
修改了Element UI中组件的样式,打包后样式丢失
前端·vue.js
XU磊26010 分钟前
深入理解表单---提交用户与网页交互的重要方式:GET 与 POST 的本质区别与应用实践
服务器·前端·javascript
爱分享的程序员13 分钟前
前端跨端框架的开发以及IOS和安卓的开发流程和打包上架的详细流程
android·前端·ios
珎珎啊24 分钟前
uniapp+vue3移动端实现输入验证码
前端·javascript·uni-app
HtwHUAT1 小时前
五、web自动化测试01
前端·css·chrome·python·功能测试·selenium·html
86Eric1 小时前
Vue 中 使用 Mixins 解决 多页面共用相同组件的相关问题
前端·javascript·vue.js·mixins·公用组件
qq_25249639961 小时前
react 子组件暴露,父组件接收
前端·javascript·react.js
fakaifa1 小时前
【最新版】西陆健身系统源码全开源+uniapp前端
前端·小程序·uni-app·开源·php·约课小程序·健身小程序
南囝coding1 小时前
关于我的第一个产品!
前端·后端·产品