Asperger
Asperger

Reputation: 3222

Animating custom shader in webgl / three.js

I am currently learning OpenGL and stumbled across this tutorial: http://patriciogonzalezvivo.com/2015/thebookofshaders/03/

I tried to use the snipped in webgl in order to further understand the mechanism but somehow it doesnt work and I am honestly not sure why. I am sure there must be some syntax error but what could it be? If not then how can I make this work?

To be honest im trying to understand how to implement u_time. I thought the GPU automatically has an in built timer which causes the color transition animation.

// 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>

Upvotes: 1

Views: 4126

Answers (1)

Le Jeune Renard
Le Jeune Renard

Reputation: 400

You were right that you need to bind/update the value in javascript. To do that, you need to do two things:

  1. Declare the u_time uniform ( including the type and initial value ) that is in the shader when you create the shader material.

    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. You need to have a render loop where you continuously update the uniform's value. Here is a basic example of a render loop which uses requestAnimationFrame() to call itself once the browser is ready to render another frame:

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

    Note that you update uniforms.u_time.value not uniforms.u_time. This is because a uniform holds both it's type and it's current value.

Working jsFiddle with changes

Also know that you are using a very old version of three.js in your fiddle. Version r40 is from 2011 and we are up to r76 currently. There are some niceties in recent versions that make this simpler.

Upvotes: 4

Related Questions