基于Babylon.js的Shader入门二(让Shader使用一个纹理)
一、顶点着色器
文件名为"BaseTexture.vertex.fx"的内容:
// 顶点着色器
attribute vec3 position;
attribute vec2 uv; // 添加纹理坐标属性
uniform mat4 worldViewProjection;
varying vec2 vUV; // 用于将纹理坐标传递给片段着色器
void main() {
gl_Position = worldViewProjection * vec4(position, 1.0);
vUV = uv; // 传递纹理坐标
}
若要让shader使用一个纹理,那么需要在顶点着色器阶段获取模型的uv,并且将该uv传递给片元着色器,顶点着色器通过attribute类型的vec2变量uv来分别获取每个顶底的uv,同时将这个uv传递给varying类型的的vec2变量vUV,下面我们再啰嗦一下attribute变量、uniform变量和varying变量之间的区别。
attribute变量、uniform变量和varying变量之间的区别
1. attribute
变量
attribute
变量是顶点着色器的输入变量,用于从应用程序(如 JavaScript 或 Babylon.js)向顶点着色器传递数据。每个顶点可以有不同的 attribute
值。
特点:
-
作用域:只能在顶点着色器中使用。
-
数据来源:由应用程序通过缓冲区(如顶点缓冲区)提供。
-
典型用途:传递顶点的位置(
position
)、法线(normal
)、纹理坐标(uv
)等逐顶点数据。
示例:
attribute vec3 position; // 顶点位置
attribute vec2 uv; // 纹理坐标
在 Babylon.js 中,attribute
变量通常通过 Mesh
的几何数据(如顶点位置、纹理坐标)自动传递。
2. varying
变量
varying
变量用于在顶点着色器和片段着色器之间传递数据。顶点着色器计算每个顶点的值,然后这些值会在图元(如三角形)的片段之间进行插值,最终传递给片段着色器。
特点:
-
作用域:可以在顶点着色器和片段着色器中使用。
-
数据来源:由顶点着色器赋值,经过插值后传递给片段着色器。
-
典型用途:传递纹理坐标(
uv
)、颜色、法线等需要在片段之间插值的数据。
示例:
// 顶点着色器
varying vec2 vUV; // 声明 varying 变量
void main() {
vUV = uv; // 从 attribute 变量赋值
}
// 片段着色器
varying vec2 vUV; // 声明相同的 varying 变量
void main() {
gl_FragColor = texture2D(textureSampler, vUV); // 使用插值后的纹理坐标
}
插值过程:
-
顶点着色器为每个顶点计算
vUV
。 -
在光栅化阶段,
vUV
会在图元的片段之间进行插值。 -
片段着色器接收插值后的
vUV
值。
3. uniform
变量
uniform
变量是全局变量,用于从应用程序向 Shader 传递常量数据。这些数据在绘制调用中不会改变(即对于所有顶点和片段都是相同的)。
特点:
-
作用域:可以在顶点着色器和片段着色器中使用。
-
数据来源:由应用程序通过 Shader 的
uniform
接口设置。 -
典型用途:传递变换矩阵(如
worldViewProjection
)、颜色(如diffuseColor
)、纹理采样器等。
示例:
uniform mat4 worldViewProjection; // 变换矩阵
uniform vec3 diffuseColor; // 颜色
uniform sampler2D textureSampler; // 纹理采样器
在 Babylon.js 中,uniform
变量可以通过 ShaderMaterial
的 setXXX
方法设置,例如:
shaderMaterial.setMatrix("worldViewProjection", matrix);
shaderMaterial.setColor3("diffuseColor", color);
shaderMaterial.setTexture("textureSampler", texture);
4. 三者的区别总结
特性 | attribute | varying | uniform |
---|---|---|---|
作用域 | 顶点着色器 | 顶点着色器和片段着色器 | 顶点着色器和片段着色器 |
数据来源 | 应用程序(顶点缓冲区) | 顶点着色器赋值并插值 | 应用程序(ShaderMaterial) |
是否可变 | 每个顶点不同 | 在图元中插值 | 绘制调用中不变 |
典型用途 | 顶点位置、法线、纹理坐标 | 插值后的纹理坐标、颜色 | 变换矩阵、颜色、纹理采样器 |
二、片元着色器
文件名为"BaseTexture.fragment.fx"
// 片段着色器
precision highp float;
uniform vec3 diffuseColor;
uniform sampler2D textureSampler; // 添加纹理采样器
varying vec2 vUV; // 接收从顶点着色器传递过来的纹理坐标
void main() {
vec4 textureColor = texture2D(textureSampler, vUV); // 采样贴图颜色
gl_FragColor = textureColor * vec4(diffuseColor, 1.0); // 将贴图颜色与原来的颜色相乘
}
再说varying变量
这里又出现了一个varying vec2 vUV变量,实际上,顶点着色器中的 vUV
和片元着色器中的 vUV
是同一个变量在不同着色器阶段的引用。它们通过 varying
关键字连接起来,用于在顶点着色器和片元着色器之间传递数据。
流程说明:
-
顶点着色器:
-
每个顶点的
uv
属性被赋值给vUV
。 -
例如,一个三角形的三个顶点的
vUV
值分别为(0, 0)
、(1, 0)
和(0.5, 1)
。
-
-
光栅化阶段:
-
在光栅化过程中,
vUV
的值会在三角形的每个片段之间进行插值。 -
例如,三角形内部的某个片段的
vUV
可能是(0.3, 0.4)
。
-
-
片元着色器:
-
每个片段的
vUV
值是通过插值计算得到的。 -
使用
texture2D
函数根据vUV
采样纹理颜色。
-
varying
变量的特点
-
同名匹配:顶点着色器和片元着色器中的
varying
变量必须同名(如vUV
),否则无法正确传递数据。 -
插值行为:
varying
变量的值会在图元的顶点之间进行插值。例如,颜色、纹理坐标等需要插值的属性通常使用varying
传递。 -
只读性:在片元着色器中,
varying
变量是只读的,不能修改它的值。
关于sampler2D纹理采样器
在 Babylon.js 中,sampler2D
类型的变量用于表示纹理采样器。Babylon.js 通过 ShaderMaterial
或 Effect
将纹理传递给 Shader,并在 Shader 中使用 texture2D
函数对纹理进行采样。以下是 Babylon.js 向 Shader 传送纹理的原理和具体使用方法。
1. Shader 中的 sampler2D
变量
在 GLSL(WebGL Shader 语言)中,sampler2D
是一种特殊的变量类型,用于表示 2D 纹理。通常,sampler2D
变量会与 texture2D
函数一起使用,以从纹理中采样颜色。
2. Babylon.js 向 Shader 传递纹理的原理
Babylon.js 通过以下步骤将纹理传递给 Shader:
-
声明
sampler2D
变量:
在 Shader 中声明一个uniform sampler2D
变量,例如textureSampler
。 -
绑定纹理到纹理单元:
Babylon.js 会将纹理绑定到一个纹理单元(Texture Unit),例如GL_TEXTURE0
、GL_TEXTURE1
等。 -
设置
sampler2D
的纹理单元索引:
Babylon.js 会将纹理单元的索引(如0
、1
等)传递给 Shader 中的sampler2D
变量。 -
在 Shader 中使用纹理:
在 Shader 中,使用texture2D
函数对纹理进行采样。
3. Babylon.js 传递纹理的内部原理
(1) 纹理单元(Texture Unit)
WebGL 支持多个纹理单元(通常至少 8 个),每个纹理单元可以绑定一个纹理。Babylon.js 会自动管理纹理单元的分配。
(2) 绑定纹理
当调用 setTexture
时,Babylon.js 会执行以下操作:
-
将纹理绑定到一个可用的纹理单元(如
GL_TEXTURE0
)。 -
将纹理单元的索引传递给 Shader 中的
sampler2D
变量。
(3) Shader 中的纹理采样
在 Shader 中,texture2D
函数会根据 sampler2D
变量绑定的纹理单元索引,从对应的纹理中采样颜色。
三、Babylon.js使用示例
// 创建 ShaderMaterial
const shaderMaterial = new BABYLON.ShaderMaterial("shader", scene,
"./src/Shader/BaseTexture",
{
attributes: ["position", "uv"], // 确保包含 uv 属性
uniforms: ["worldViewProjection", "diffuseColor", "textureSampler"], // 确保包含纹理采样器
});
// 设置颜色
shaderMaterial.setColor3("diffuseColor", new BABYLON.Color3(1, 0, 0)); // 例如红色
// 加载纹理
const texture = new BABYLON.Texture("path/to/texture.png", scene);
shaderMaterial.setTexture("textureSampler", texture); // 设置纹理
// 应用材质到网格
const mesh = BABYLON.MeshBuilder.CreateBox("box", {}, scene);
mesh.material = shaderMaterial;