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


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

    在这里插入图片描述
    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);
    }
    
    • 1
    • 2
    • 3

    该函数必须命名为 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);
    }
    
    • 1
    • 2
    • 3

    同样,该函数需要命名为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
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    并访问脚本中的 DOM 元素:

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

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

    信息从顶点着色器传递到片段着色器,在片段着色器中,根据像素相对于顶点的位置对值进行插值。例如,如果为每个顶点指定一种颜色并将其向下传递到片段着色器,则像素颜色将从顶点颜色中插值。
    在这里插入图片描述

    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
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后在渲染函数中,我们可以更新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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4、GLSL Varyings

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

    varying vec2 vUv;
    
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在片段着色器中访问它:

    varying vec2 vUv;
    uniform sampler2D uImage;
    
    void main() {
      gl_FragColor = texture2D(uImage, vUv);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在脚本中设置纹理:

    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';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我通常用 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));
    
    • 1
    • 2
    • 3

    可以添加一个标准的 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));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在着色器中访问属性:

    attribute float displacement;
    
    void main() {
      vec3 newPosition = position + normal * vec3(displacement);
      gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6、GLSL模块

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

    npm install glslify glsl-noise
    
    • 1

    将着色器包装在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);
      }
    `);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7、Webpack GLSL加载器

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

    npm install raw-loader glslify-loader
    
    • 1

    Webpack配置看起来像这样:

    module: {
      rules: [
        {
          test: /\.(glsl|vs|fs|vert|frag)$/,
          exclude: /node_modules/,
          use: ['raw-loader', 'glslify-loader']
        }
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    8、Parcel GLSL加载器

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

    9、GLSL着色器示例

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


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

  • 相关阅读:
    ONLYOFFICE 8.0:引领数字化办公新纪元
    向量数据库技术全景
    新手也能上手的天气预报demo(高德API+echarts)
    新书推荐——华为·无线局域网应用技术(微课版丨第2版)
    人工智能基础:机器学习常见的算法介绍
    Linux内核中竞态学习过程的问题
    mvc-servlet
    MySQL日志管理、备份与恢复
    使用Vitis HLS创建属于自己的IP
    【二十五】springboot整合jedis和redisson布隆过滤器处理缓存穿透
  • 原文地址:https://blog.csdn.net/shebao3333/article/details/127405832