1,什么是着色器

着色器(Shader)是计算机图形学中的一个重要概念,它是在 GPU 上运行的程序,用于计算三维场景中每个像素的颜色和其他属性。

着色器通常分为两种类型:顶点着色器和片元着色器。顶点着色器主要用于对每个顶点进行操作,将其变换到最终渲染的位置,并将一些属性传递到片元着色器中。片元着色器主要用于对每个像素进行操作,计算像素的颜色值,并返回给渲染引擎。

着色器可以帮助我们实现许多高级渲染效果,如阴影、光照、纹理、模糊、反射、折射等等。通过编写自定义的着色器程序,我们可以控制图形中的每一个像素的颜色和属性,从而创建更加逼真和高效的渲染效果。

在 web 开发中,着色器通常使用 WebGL 技术来实现。three.js 是一个流行的 WebGL 库,它提供了一套易用的 API 和内置的着色器,使得开发者可以轻松地实现各种高级的渲染效果。

2,Three.js 着色器示例

下面代码中,顶点着色器和片元着色器的代码都是 GLSL 语言编写的。顶点着色器主要是对每个顶点进行操作,将其变换到最终渲染的位置,并将一些属性传递到片元着色器中。片元着色器主要是对每个像素进行操作,计算像素的颜色值,并返回给渲染引擎。

顶点着色器代码中,varying 变量 vPosition 用于将顶点位置从顶点着色器传递到片元着色器。modelViewMatrix 和 projectionMatrix 是 three.js 中的内置变量,分别用于将顶点从模型空间转换到视图空间和投影空间。gl_Position

片元着色器代码中,varying 变量 vPosition 是从顶点着色器传递过来的。gl_FragColor

在最后,我们通过将着色器材质应用于网格对象来应用着色器。这个着色器使用了我们编写的顶点着色器和片元着色器。

// 顶点着色器代码
const vertexShader = `
  // 顶点位置,传递到片元着色器
  varying vec3 vPosition;

  void main() {
    // 顶点位置
    vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
    // 计算顶点位置
    gl_Position = projectionMatrix * modelViewPosition;
    // 将顶点位置传递到片元着色器
    vPosition = position;
  }
`;

// 片元着色器代码
const fragmentShader = `
  // 从顶点着色器传递的顶点位置
  varying vec3 vPosition;

  void main() {
    // 将颜色设置为红色
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
`;

// 创建着色器材质
const material = new THREE.ShaderMaterial({
  vertexShader,
  fragmentShader,
});

// 创建网格对象并应用着色器材质
const geometry = new THREE.BoxGeometry();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

3,Three.js使用着色器实现控制平面网格的动态效果案例

这个案例使用了一个顶点着色器和一个片元着色器来控制平面网格的动态效果。顶点着色器中计算了一个位移量,根据时间和顶点的位置来更新顶点的位置,实现了网格的动态效果;片元着色器中输出了一个纯色,用来渲染网格的颜色。在渲染循环中,通过更新uniform变量来控制网格的动态效果。

// 创建一个平面几何体
var geometry = new THREE.PlaneGeometry(5, 5, 5, 5);

// 创建一个着色器材质
var material = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 0.0 }, // 时间
    amplitude: { value: 1.0 }, // 振幅
    color: { value: new THREE.Color(0xff0000) } // 颜色
  },
  vertexShader: `
    uniform float time;
    uniform float amplitude;

    // 顶点着色器函数
    void main() {
      vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
      gl_Position = projectionMatrix * mvPosition;

      // 计算位移
      float displacement = amplitude * sin(time + mvPosition.y);
      // 更新顶点位置
      mvPosition.y += displacement;
      // 输出新位置
      gl_Position = projectionMatrix * mvPosition;
    }
  `,
  fragmentShader: `
    uniform vec3 color;

    // 片元着色器函数
    void main() {
      gl_FragColor = vec4(color, 1.0);
    }
  `
});

// 创建一个平面网格
var mesh = new THREE.Mesh(geometry, material);

// 将平面网格添加到场景中
scene.add(mesh);

// 在渲染循环中更新uniform变量
function animate() {
  requestAnimationFrame(animate);
  var time = Date.now() * 0.001; // 获取当前时间
  material.uniforms.time.value = time; // 更新时间
  mesh.rotation.x += 0.01; // 旋转平面网格
  mesh.rotation.y += 0.02;
  renderer.render(scene, camera);
}
animate();