Three.js着色器基础【含源码】

着色器(Shader)是在 GPU 上运行的程序。它们被称为着色器的原因是,最初它们只处理3D对象的着色,但后来扩展到了3D对象之外。它们需要与传统编程不同的思维方式,因为程序是针对每个顶点或像素并行运行的。

Three.js着色器基础【含源码】_第1张图片
WebGL和OpenGL使用一种名为 GLSL 的语言,它代表OpenGL 着色器语言,类似于 C 语言。在 Three.js 中添加着色器的最简单方法是使用ShaderMaterial。还有一些RawShaderMaterial,它没有包括一些three.js GLSL代码。

1、顶点着色器和片段着色器

在 WebGL 中有顶点(Vertex)着色器和片段(Fragment)着色器。在顶点着色器中,你可以操作几何图形的顶点,在片段着色器中,你可以操作渲染的三角形的像素。在实时图形中,一切都被简化为三角形。顶点着色器返回 2D 位置,因此它使用投影矩阵从 3D 投影到 2D。基本顶点着色器将如下所示:

void main() {
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

该函数必须命名为 main,并且你必须设置内置变量gl_Position 。projectionMatrix、modelViewMatrix和position由three.js提供。从右到左查看矩阵乘法。 modelViewMatrix是视图矩阵要相乘的模型矩阵。视图矩阵是相机的反变换 - 移动相机和反方向移动模型等效。模型矩阵是在模型上完成的转换。projectionMatrix 负责从3D到2D的投影。

基本片段着色器将如下所示:

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

同样,该函数需要命名为main,并且需要设置内置变量gl_FragColor 。在上例中,我们将每个像素设置为红色。要创建着色器材料,请将顶点着色器和片段着色器指定为字符串,否则three.js将使用默认着色器。

const vertexShader = /*glsl*/`
void main() {
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;

const fragmentShader = /*glsl*/`
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

const material = new ShaderMaterial({
  fragmentShader: fragmentShader,
  vertexShader: vertexShader
});

如果你在 VS Code 中安装了标记Comment tagged templates和 Shader language support插件,则在上面的代码中字符串前面的/glsl/注释会添加语法突出显示。使用模板文本,即反引号,可以在字符串中包含多行。你还可以在 HTML 文件的脚本标记中包含 GLSL:


并访问脚本中的 DOM 元素:

const material = new ShaderMaterial({
  fragmentShader: document.getElementById('fragment-shader').textContent
});

或者,你可以将着色器保存在单独的文件中,并在使用 Webpack 或 Parcel 等打包工具时导入它们。对于Webpack,你需要安装raw loader,对于Parcel,你需要安装@parcel/transformer-glsl插件。

信息从顶点着色器传递到片段着色器,在片段着色器中,根据像素相对于顶点的位置对值进行插值。例如,如果为每个顶点指定一种颜色并将其向下传递到片段着色器,则像素颜色将从顶点颜色中插值。
Three.js着色器基础【含源码】_第2张图片

2、着色器变量

着色器具有几种不同类型的变量:

  • Uniforms:这些在所有GPU线程中都是相同的,例如当前时间。你可以在ShaderMaterial中设置这些设置。
  • Varyings:这些因每个 GPU 线程而异。你可以使用它们将值从顶点着色器传递到片段着色器,例如UV(纹理映射坐标)。可以在顶点着色器中设置这些设置。
  • Attributes:这是加载到顶点着色器中的数据,例如顶点的位置。three.js通常会处理这些内容,除非我们要添加自定义属性或想要手动指定顶点。可以在几何图形上设置这些值。

3、GLSL Uniforms

Uniforms是将数据从 JavaScript 传递到着色器的一种方法。

this.material = new ShaderMaterial({
  uniforms: {
    uTime: { value: 0 }
  },
  fragmentShader: fragmentShader
});

然后在渲染函数中,我们可以更新Uniforms值:

render() {
this.material.uniforms.uTime.value++;
this.renderer.render(this.scene, this.camera);
}
在顶点或片段着色器中,可以像这样访问它:

uniform float uTime

void main() {
  gl_FragColor = vec4(vec3(abs(sin(uTime))), 1.0);
}

4、GLSL Varyings

Varyings对于将值从顶点着色器传递到片段着色器非常有用。下面,我将一个Varying设置为three.js内置变量:

varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

在片段着色器中访问它:

varying vec2 vUv;
uniform sampler2D uImage;

void main() {
  gl_FragColor = texture2D(uImage, vUv);
}

在脚本中设置纹理:

const image = new Image();
image.onload = function() {
  const texture = new Texture(image);
  const material = new ShaderMaterial({
    uniforms: {
      uTexture: { value: texture }
    },
    fragmentShader
  });
}
image.src = '/images/some_image.jpg';

我通常用 u 作为 uniform 的前缀,用 v 作为 varying 的前缀,但这不是必需的。

5、GLSL Attributes

设置属性时,Three.js 要求一个类型化数组。类型化数组就是由于WebGL的需要而被添加到了JavaScript中。 默认情况下,BufferGeometry提供position 、normal 和uv属性。如果要使用顶点颜色,则需要在材质的选项中设置一个称为color 的属性并将vertexColors设置为 true。

const geometry = new BufferGeometry();
const displacement = new Float32Array([0, 0.5, 1]);
geometry.setAttribute('displacement', new BufferAttribute(displacement, 1));

可以添加一个标准的 JavaScript 数组,并用three.js将其转换为类型化数组。如果以编程方式构造数组,则可能需要执行此操作:

const vertices = [];
for (let i = 0; i < 3; i++) {
  vertices.push(Math.cos(i), Math.sin(i), 0);
}
geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3));

在着色器中访问属性:

attribute float displacement;

void main() {
  vec3 newPosition = position + normal * vec3(displacement);
  gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}

6、GLSL模块

节点模块在编写JavaScript时非常有用。GLSL没有一个官方的模块系统,但有一个非官方的模块系统,叫做Glslify。首先,需要将Glslify模块与 GLSL 模块一起安装:

npm install glslify glsl-noise

将着色器包装在glsl函数中,并将 GLSL 模块导入着色器中:

import glsl from 'glslify';

const fragmentShader = glsl(`
  #pragma glslify: noise = require('glsl-noise/simplex/3d');

  varying vec3 vPosition;

  void main() {
    gl_FragColor = vec4(noise(vPosition), 1.0);
  }
`);

7、Webpack GLSL加载器

有一个用于webpack的 Glslify 加载器,因此可以在外部着色器文件中使用 Glslify。

npm install raw-loader glslify-loader

Webpack配置看起来像这样:

module: {
  rules: [
    {
      test: /\.(glsl|vs|fs|vert|frag)$/,
      exclude: /node_modules/,
      use: ['raw-loader', 'glslify-loader']
    }
  ]
}

8、Parcel GLSL加载器

Parcel通过@parcel/transformer-glsl插件支持Glslify。

9、GLSL着色器示例

下面是具有顶点着色器和片段着色器的球体示例。顶点着色器使用噪声函数置换顶点,片段着色器使用噪声函数再次混合红色、绿色和蓝色。默认情况下,几何图形中的顶点处于断开连接状态。如果移动顶点,它只会针对它所附着的面移动它,因此面将断开连接。要保持面连接,你必须将顶点转换为索引顶点,即通过索引引用相同的顶点,而不是为每个面复制它。将着色器变量作为uniform的一个优点是,你可以附加 GUI 并对其进行调整。可以在此处查看该示例的源代码。
Three.js着色器基础【含源码】_第3张图片


原文链接:Three.js着色器基础 — BimAnt

你可能感兴趣的:(数字孪生,javascript,着色器,算法)