Three.js 着色器基础:顶点与片元着色器详解

作者:小圆点时间:2026-10-26 23:42:24浏览:377

本文深入解析 Three.js 中的着色器机制,涵盖顶点着色器与片元着色器的核心功能,以及 Uniforms、Attributes 和 Varyings 三种变量的应用。通过简单的 Hello World 示例,展示如何创建和使用着色器材质,为后续复杂图形效果开发奠定基础。

译序

Three.js 与 WebGL 基础

Three.js 是一款优秀的开源 WebGL 库,它允许 JavaScript 直接操作 GPU,从而在浏览器中实现真正的 3D 渲染。然而,该技术目前仍处于发展阶段,相关学习资料相对稀缺,开发者通常需要依靠 Demo 源码和 Three.js 源代码进行深入学习。



0. 简介

此前已发布过《开始使用 Three.js》一文。若未阅读,建议先查阅该基础教程,因为本文内容建立在其知识体系之上。



本文将重点探讨着色器。在 Three.js 简化开发流程之前,原生 WebGL 已具备强大能力。若希望实现特定视觉效果或深入探究屏幕渲染细节,着色器是必经之路。如果你追求比基础教程更丰富的视觉效果,本文将为你讲解 Three.js 如何处理那些繁琐的底层工作。


在正式进入代码之前,需说明本文将花费大量篇幅解释着色器代码原理。后续教程将在此基础上进一步利用着色器实现具体功能,因为着色器初看晦涩难懂,需要细致的拆解与解释。


1. 两种着色器

着色器类型:顶点与片元

WebGL 摒弃了固定的渲染管线,开发者无法直接使用封装好的“黑盒”着色器(注:上世纪显卡主要支持固定管线)。WebGL 提供的是可编程渲染管线,这种方式功能更强大,但理解和使用难度也更高。简而言之,可编程管线要求开发者自行负责获取顶点数据并将其绘制到屏幕上。着色器是渲染管线的关键组成部分,主要包含两种类型:


1. 顶点着色器


2. 片元着色器


需要明确的是,这两种着色器均在显卡 GPU 上运行。我们将处理数据从 CPU 转移至 GPU,从而减轻 CPU 负担。现代 GPU 针对着色器所需的运算类型进行了大幅优化,这种架构设计极具价值。


2. 顶点着色器

顶点着色器详解

基元形状(如球体)由顶点构成。顶点着色器会依次接收这些顶点数据并进行处理。虽然每个顶点的处理逻辑可自由定制,但顶点着色器必须完成一项核心任务:为名为 gl_Position 的变量赋值。该变量是一个四维数组,用于表示顶点在屏幕上的最终位置。这一过程涉及将三维坐标(包含 x、y、z 值的顶点)投影到二维屏幕,这是一个复杂的数学转换。幸运的是,使用 Three.js 等工具可以方便地访问 gl_Position。


3. 片元着色器

片元着色器与片元概念

当拥有包含顶点的三维物体后,下一步是将其投影到二维屏幕。此时,颜色、纹理和光照信息从何而来?这正是片元着色器负责处理的领域。


与顶点着色器类似,片元着色器也必须完成一项强制任务:设置或清除 gl_FragColor 变量。这是一个四维浮点变量,代表片元点的最终颜色。那么,什么是片元?想象一个由三个顶点构成的三角形,片元是指经过这三个顶点计算后,位于三角形内部的所有像素点。因此,片元的值由顶点值内插生成。例如,若一个顶点颜色为红色,相邻顶点为蓝色,则会在红色顶点附近观察到颜色渐变,由红变紫,最终在蓝色顶点附近呈现蓝色。


4. 着色器变量

着色器变量类型:Uniforms、Attributes 与 Varyings

关于着色器变量,主要有三种类型:Uniforms、Attributes 和 Varyings。初次接触这些术语时可能会感到困惑,因为它们与以往使用的概念不同。现在可以这样理解它们:


1. Uniforms 变量既可传入顶点着色器,也可传入片元着色器。它们包含在整个渲染过程中保持不变的数值,例如点光源的位置。


2. Attributes 变量对应于每个顶点,且仅能传入顶点着色器。例如,每个顶点可以拥有独立的颜色属性。Attributes 变量与顶点之间是一一对应的关系。


3. Varyings 变量在顶点着色器中定义,并准备传入给片元着色器。为确保数据传递正确,必须保证两个着色器中该变量的类型和命名完全一致。一个经典应用场景是法线向量,因为在计算光照时需要用到法线信息。


在后续教程中,我将演示如何使用这三种变量,并展示它们在实际应用中的具体用法。


至此,我们已经讨论了顶点着色器、片元着色器以及三种着色器变量。接下来,让我们看一个最简单的着色器示例。


5. Hello World(译者吐槽:能不能不要秀法语啊)

Hello World:最简单的着色器示例

以下是一个最简单的顶点着色器代码:


代码如下:

 
/** 
* 每个顶点坐标乘以模型视图矩阵,再乘以投影矩阵
* 获得在二维屏幕上的坐标
*/ 
void main() {
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
} 

以下是一个最简单的片元着色器代码:


代码如下:

 
/** 
* 将任意一个像元色设置为粉红色
*/ 
void main() {
gl_FragColor = vec4(1.0, // R
0.0, // G
1.0, // B
1.0); // A
} 

以上就是全部代码。如果直接运行,你将在屏幕上看到一个“无光照”效果的粉红色几何体。实现起来并不复杂,对吗?



在顶点着色器中,我们通过 Three.js 传入了一些 Uniforms 变量。这里有两个 4×4 的矩阵 Uniforms 变量:模型视图矩阵和投影矩阵。你无需深入理解这两个矩阵的具体运算原理。简单来说,这两个矩阵描述了三维点坐标如何投影成为二维屏幕上的坐标。



实际上,我只介绍了这两段简短的代码。Three.js 已将这些代码预先嵌入到你的着色器代码之前,因此你无需担心。事实上,Three.js 还在你的代码前添加了许多其他内容,例如光照数据、节点颜色和节点法向量等。如果没有 Three.js 的封装,你需要亲自创建并设置这些对象,工作量将显著增加。


6. 使用着色器材质

使用着色器材质

代码如下:

 
/** 
* 假设我们可以使用 jQuery
* 将着色器的代码文本从 DOM 中抽取出来
*/ 
var vShader = $('vertexshader');
var fShader = $('fragmentshader');
var shaderMaterial =
new THREE.ShaderMaterial({
vertexShader: vShader.text(),
fragmentShader: fShader.text()
}); 

从这一步开始,Three.js 将编译并运行你的着色器,将其连接到你创建的材质上,而材质又依附于你创建的 Mesh 对象。虽然过程看似简单,但在浏览器中进行 3D 编程确实具有一定复杂性,这符合预期。



我们还可以向着色器材质添加另外两种属性:Uniforms 和 Attributes。它们可以是向量、整数或浮点数。如前所述,Uniforms 变量在所有点的计算过程中保持不变,因此它们更可能是单一值;而 Attributes 变量针对每个顶点,因此应当是数组。在 Mesh 中,Attribute 变量与顶点是一一对应的关系。


7. 小结

本篇教程到此结束。虽然内容较多,但许多方面仅做了浅尝辄止的介绍。在下一篇教程中,我将提供一个复杂的着色器示例,通过传入 Attributes 变量和 Uniforms 变量来模拟光照效果。


我已将本篇教程的源码打包,你可以下载作为参考。

作者最新文章