You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WebGL/GLSL中实现类似ShaderToy的iTime时间变量动画?

嘿,要实现类似ShaderToy里的iTime动画效果其实很简单,核心就是给着色器添加一个时间uniform变量,然后在JS里循环更新这个变量并重新绘制画面。我给你一步步拆解修改:

1. 修改着色器,添加时间变量

首先在顶点或片元着色器里声明一个全局的uniform float变量(对应ShaderToy的iTime),你可以用它修改顶点位置、颜色等属性来实现动画。下面是修改后的代码示例:

修改后的顶点着色器(可选添加顶点动画)

attribute vec4 a_position;
uniform float u_time; // 新增时间uniform变量
varying vec4 v_color;
void main() {
  // 用时间让顶点做小幅偏移动画
  vec2 animatedPos = a_position.xy + vec2(sin(u_time) * 0.2, cos(u_time) * 0.2);
  gl_Position = vec4(animatedPos, 0.0, 1.0);
  v_color = gl_Position * 0.5 + 0.5;
}

修改后的片元着色器(用时间修改颜色)

precision mediump float;
uniform float u_time; // 新增时间uniform变量
varying vec4 v_color;
void main() {
  // 让颜色随时间做色相偏移
  vec3 hueCycle = vec3(sin(u_time), cos(u_time), sin(u_time + 1.57));
  gl_FragColor = vec4(v_color.rgb * hueCycle * 0.5 + 0.5, 1.0);
}

2. 在JS代码中实现动画循环

接下来要在JS里完成三个关键操作:获取uniform变量的位置、创建动画循环、每一帧更新时间并重新绘制。修改后的JS代码如下:

var gl = document.getElementById("canvas").getContext('webgl');
var vertexShader = createShader(gl, gl.VERTEX_SHADER, window.vert);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, window.frag);
var program = createProgram(gl, vertexShader, fragmentShader);

// --- 新增:获取时间uniform的位置 ---
var timeUniformLocation = gl.getUniformLocation(program, "u_time");

var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
  -1, -1,
  -1, 1,
  1, 1,
  1, 1,
  1, -1,
  -1, -1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
resizeCanvas(gl);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

// --- 新增:动画循环函数 ---
function animate(currentTime) {
  // 将毫秒转成秒,和ShaderToy的iTime单位保持一致
  var timeInSeconds = currentTime * 0.001;

  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  
  gl.useProgram(program);
  // --- 新增:把当前时间传入着色器 ---
  gl.uniform1f(timeUniformLocation, timeInSeconds);

  gl.enableVertexAttribArray(positionAttributeLocation);
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  var size = 2;
  var type = gl.FLOAT;
  var normalize = false;
  var stride = 0;
  var offset = 0;
  gl.vertexAttribPointer(
    positionAttributeLocation, size, type, normalize, stride, offset);

  var primitiveType = gl.TRIANGLES;
  var offset = 0;
  var count = 6;
  gl.drawArrays(primitiveType, offset, count);

  // 持续请求下一帧动画
  requestAnimationFrame(animate);
}

// --- 启动动画循环 ---
requestAnimationFrame(animate);

3. 关键细节说明

  • Uniform变量:这是JS向着色器传递全局数据的标准方式,适合时间、画布分辨率这类每一帧都可能变化的全局参数。
  • 时间单位转换:requestAnimationFrame传入的currentTime是毫秒数,转成秒后和ShaderToy的iTime行为完全一致,方便你复用ShaderToy里的时间相关逻辑。
  • 动画循环:用requestAnimationFrame替代setInterval,它会和浏览器的刷新频率同步,避免画面卡顿。

这样修改后,你就能看到画面随时间动起来了!你可以自由调整着色器里的时间逻辑,比如用u_time计算缩放、旋转、波形动画等,玩法和ShaderToy几乎一致。

内容的提问来源于stack exchange,提问作者Mudassir Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:55:55