基于Babylon.js的Shader入门三(让Shader中的纹理动起来)
本来是做一个纹理偏移的效果,不过感觉应该很无聊,最后觉得还是做一个FlipBook效果吧,该效果使用了一张5*5的爆炸动画序列纹理,如下:
最终呈现效果参考:
该部分内容的顶点着色器与 Babylon.js的Shader入门二(让Shader使用一个纹理)中的内容没有区别(即本次顶点着色器名称为FlipBook.vertex.fx与上一篇中的BaseTexture.vertex.fx内容相同),下面直接展示片元着色器的内容改变。
片元着色器
FlipBook.fragment.fx文件内容:
precision highp float;
uniform sampler2D textureSampler; // 5x5 动画序列纹理
uniform float rows; // 序列帧行数
uniform float cols; // 序列这列数
uniform float time; // 时间变量,用于控制动画播放
varying vec2 vUV;
void main() {
// 计算总帧数
float totalFrames = rows * cols;
// 根据时间计算当前帧索引
float frameIndex = mod(time * 10.0, totalFrames); // 假设每秒播放 10 帧
frameIndex = floor(frameIndex);
// 计算当前帧的行列索引
float rowIndex = floor(frameIndex / cols);
float colIndex = mod(frameIndex, cols);
// 计算纹理坐标偏移
vec2 frameSize = vec2(1.0 / cols, 1.0 / rows); // 每帧的纹理大小
vec2 offset = vec2(colIndex * frameSize.x, 1.0 - (rowIndex + 1.0) * frameSize.y); // 注意 Y 轴方向
// 调整纹理坐标
vec2 flipbookUV = vUV * frameSize + offset;
// 采样纹理
vec4 color = texture2D(textureSampler, flipbookUV);
gl_FragColor = color;
}
JavaScript 实现
在 Babylon.js 中,你需要将时间变量 time
传递给 Shader,并更新它的值。
创建 ShaderMaterial
const shaderMaterial = new BABYLON.ShaderMaterial("shader", scene,
"./src/Shader/FlipBook",
{
attributes: ["position", "uv"],
uniforms: ["worldViewProjection", "textureSampler", "time"], // 添加 time 变量
});
// 加载纹理
const texture = new BABYLON.Texture("path/to/texture.png", scene);
shaderMaterial.setTexture("textureSampler", texture);
// 初始化时间变量
let time = 0;
shaderMaterial.setFloat("time", time);
//设置爆炸序列图片行数
shaderMaterial.setFloat("rows", 5);
//设置爆炸序列图片列数
shaderMaterial.setFloat("cols", 5);
// 创建网格并应用材质
const mesh = BABYLON.MeshBuilder.CreateBox("box", {}, scene);
mesh.material = shaderMaterial;
更新时间变量
在渲染循环中更新 time
变量,并传递给 Shader。
scene.registerBeforeRender(() => {
time += scene.getEngine().getDeltaTime() / 1000; // 更新时间,单位为秒
shaderMaterial.setFloat("time", time);
});
原理说明
(1) 动画帧索引计算
-
根据时间
time
和帧率(如每秒 10 帧)计算当前帧索引frameIndex
。 -
使用
mod
和floor
函数确保帧索引在有效范围内(0 到 24,因为 5x5 纹理有 25 帧)。
(2) 行列索引计算
-
根据帧索引
frameIndex
计算当前帧的行索引rowIndex
和列索引colIndex
。 -
行索引:
rowIndex = floor(frameIndex / cols)
-
列索引:
colIndex = mod(frameIndex, cols)
(3) 纹理坐标调整
-
每帧的纹理大小:
frameSize = vec2(1.0 / cols, 1.0 / rows)
-
计算当前帧的纹理坐标偏移:
offset = vec2(colIndex * frameSize.x, 1.0 - (rowIndex + 1.0) * frameSize.y)
-
注意 Y 轴方向需要翻转,因为纹理坐标的原点在左下角。
-
-
调整纹理坐标:
flipbookUV = vUV * frameSize + offset
(4) 采样纹理
-
使用调整后的纹理坐标
flipbookUV
采样纹理。