【问题标题】:Animating custom shader in webgl / three.js在 webgl / three.js 中动画自定义着色器
【发布时间】:2025-11-23 08:40:01
【问题描述】:

我目前正在学习 OpenGL,偶然发现了本教程: http://patriciogonzalezvivo.com/2015/thebookofshaders/03/

我尝试使用 webgl 中的片段来进一步了解该机制,但不知何故它不起作用,老实说我不知道​​为什么。我确信一定有一些语法错误,但它可能是什么?如果没有,那我该如何做呢?

说实话,我试图了解如何实现 u_time。我认为 GPU 会自动有一个内置计时器,它会导致颜色过渡动画。

// set the scene size
var WIDTH = 400,
    HEIGHT = 300;

// set some camera attributes
var VIEW_ANGLE = 45,
    ASPECT = WIDTH / HEIGHT,
    NEAR = 0.1,
    FAR = 10000;

// get the DOM element to attach to
// - assume we've got jQuery to hand
var $container = $('#container');

// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.Camera(  VIEW_ANGLE,
                              ASPECT,
                              NEAR,
                              FAR  );
var scene = new THREE.Scene();

// the camera starts at 0,0,0 so pull it back
camera.position.z = 300;

// start the renderer
renderer.setSize(WIDTH, HEIGHT);

// attach the render-supplied DOM element
$container.append(renderer.domElement);

// create the sphere's material
var shaderMaterial = new THREE.MeshShaderMaterial({
  vertexShader:   $('#vertexshader').text(),
  fragmentShader: $('#fragmentshader').text()
});

// set up the sphere vars
var radius = 50, segments = 16, rings = 16;

// create a new mesh with sphere geometry -
// we will cover the sphereMaterial next!
var sphere = new THREE.Mesh(
  new THREE.Sphere(radius, segments, rings),
  shaderMaterial);

// add the sphere to the scene
scene.addChild(sphere);

// draw!
renderer.render(scene, camera);
<div id="container"></div>

<script type="x-shader/x-vertex" id="vertexshader">
// switch on high precision floats
#ifdef GL_ES
precision highp float;
#endif

void main()
{
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>

<script type="x-shader/x-fragment" id="fragmentshader">

uniform float u_time;

void main() {
  gl_FragColor = vec4(sin(u_time),0.0,0.0,1.0);
}

</script>
<script src="https://aerotwist.com/static/tutorials/an-introduction-to-shaders-part-1/demo/js/Three.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>

【问题讨论】:

  • 你在哪里声明 projectionMatrix、modelViewMatrix 和 position ? projectionMatrix 和 modelViewMatrix 应声明为制服;位置作为属性,另外你应该将它们发送到 gpu。如果这不是你的问题,那么你应该澄清我猜是什么不起作用。
  • 顺便说一句,我不知道 THREE.js,但我也没有看到你将 u_time 制服发送到哪里。
  • @Zouch 我正在学习教程。我假设我必须以某种方式在 javascript 代码中绑定 u_time。创建某种计时器。
  • Three.js 自动声明并更新projectionMatrixmodelViewMatrixposition 属性。

标签: three.js webgl


【解决方案1】:

您是对的,您需要在 javascript 中绑定/更新值。为此,您需要做两件事:

  1. 在创建着色器材质时声明位于着色器中的 u_time 统一(包括 type 和初始 value)。

    var shaderMaterial = new THREE.MeshShaderMaterial({
      uniforms: { // <- This is an object with your uniforms as keys
        u_time: { type: "f", value: 0 }
      },
      vertexShader:   $('#vertexshader').text(),
      fragmentShader: $('#fragmentshader').text()
    });
    
  2. 您需要有一个渲染循环,您可以在其中不断更新制服的value。这是一个渲染循环的基本示例,当浏览器准备好渲染另一个帧时,它使用requestAnimationFrame() 调用自身:

    function draw () {
      requestAnimationFrame(draw);
    
      // Update shader's time
      sphere.materials[0].uniforms.u_time.value += 0.01;
    
      // draw!
      renderer.render(scene, camera);
    }
    
    draw();
    

    请注意,您更新的是 uniforms.u_time.value 而不是 uniforms.u_time。这是因为制服同时包含 type 和当前的 value

Working jsFiddle with changes

还知道您在小提琴中使用的是非常旧版本的 three.js。 r40 版本是 2011 年的,我们目前达到 r76。最近的版本中有一些细节让这变得更简单。

【讨论】: