最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do you update a uniform In Three.js? - Stack Overflow

programmeradmin0浏览0评论

How do you update a shader uniform in three.js?

Let's say the uniform is defined as

new THREE.ShaderMaterial({
  uniforms: {
    yourUniform: { value: 'Something' }
  }
});

How do you update a shader uniform in three.js?

Let's say the uniform is defined as

new THREE.ShaderMaterial({
  uniforms: {
    yourUniform: { value: 'Something' }
  }
});
Share Improve this question edited Jan 17 at 15:49 General Grievance 4,98838 gold badges37 silver badges55 bronze badges asked Apr 28, 2015 at 20:10 Kyle PaulsenKyle Paulsen 1,0061 gold badge9 silver badges23 bronze badges 0
Add a comment  | 

5 Answers 5

Reset to default 12

This seems to work:

yourMesh.material.uniforms.yourUniform.value = whatever;

There are 2 Ways:

  • You can update uniform in Shader Material itself

    OR

  • you can use mesh.material to access the ShaderMaterialand then update the uniform

    Example of both cases:

       var  delta = 0
       var customUniforms = {
        delta: { value: 0 },
        u_time: { value: Date.now() }
       };
    
        // shader material with custom Uniform
        shaderMaterial = new THREE.ShaderMaterial({
          uniforms: customUniforms,
          vertexShader: document.getElementById("vertexShader2").textContent,
          fragmentShader: document.getElementById("fragmentShader2").textContent
        });
    
        // test Mesh object for shader
        var geometry = new THREE.BoxBufferGeometry(10, 10, 10, 10, 10, 10);
        shaderMesh = new THREE.Mesh(geometry, shaderMaterial);
        this.scene.add(shaderMesh);
    

    In animate loop

      animate = () => {
        delta += 0.1; 
    
              // Update uniform in Shader Material
              shaderMaterial.uniforms.delta.value = 0.5 + Math.sin(delta) * 0.0005;
    
              // Update uniform from Mesh itself
         shaderMesh.material.uniforms.u_time.value = delta;
    
    }
    

https://codesandbox.io/s/autumn-http-e9wk5

Complete Example

<body>
    <div id="container"></div>
    <script src="js/three.min.js"></script>
    <script id="vertexShader" type="x-shader/x-vertex">
        void main() {
            gl_Position = vec4( position, 1.0 );
        }
    </script>
    <script id="fragmentShader" type="x-shader/x-fragment">
        uniform vec2 u_resolution;
        uniform float u_time;

        void main() {
            vec2 st = gl_FragCoord.xy/u_resolution.xy;
            gl_FragColor=vec4(st.x,st.y,0.0,1.0);
        }
    </script>
    <script>
        var container;
        var camera, scene, renderer;
        var uniforms;

        init();
        animate();

        function init() {
            container = document.getElementById( 'container' );

            camera = new THREE.Camera();
            camera.position.z = 1;

            scene = new THREE.Scene();

            var geometry = new THREE.PlaneBufferGeometry( 2, 2 );

            uniforms = {
                u_time: { type: "f", value: 1.0 },
                u_resolution: { type: "v2", value: new THREE.Vector2() },
                u_mouse: { type: "v2", value: new THREE.Vector2() }
            };

            var material = new THREE.ShaderMaterial( {
                uniforms: uniforms,
                vertexShader: document.getElementById( 'vertexShader' ).textContent,
                fragmentShader: document.getElementById( 'fragmentShader' ).textContent
            } );

            var mesh = new THREE.Mesh( geometry, material );
            scene.add( mesh );

            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio( window.devicePixelRatio );

            container.appendChild( renderer.domElement );

            onWindowResize();
            window.addEventListener( 'resize', onWindowResize, false );

            document.onmousemove = function(e){
              uniforms.u_mouse.value.x = e.pageX
              uniforms.u_mouse.value.y = e.pageY
            }
        }

        function onWindowResize( event ) {
            renderer.setSize( window.innerWidth, window.innerHeight );
            uniforms.u_resolution.value.x = renderer.domElement.width;
            uniforms.u_resolution.value.y = renderer.domElement.height;
        }

        function animate() {
            requestAnimationFrame( animate );
            render();
        }

        function render() {
            uniforms.u_time.value += 0.05;
            renderer.render( scene, camera );
        }
    </script>
</body>

Little example updating the shader uniform.

/* Vertex Shader */
<script type="x-shader/x-fragment" id="myShader">
    uniform float myuniform;
    uniform sampler2D myTexture;
    varying vec2 vUV;
    varying vec2 ver;

    ver = uv *vec2( myuniform,  myuniform); // this will be updated on mouse move        

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

/* Set uniform */
var myUniform;
var myTexture = new THREE.ImageUtils.loadTexture( './data/textures/theTexture.jpg'  );
    _uniforms = {
    myUniform:  { type: "f", value: myUniform },        
    myTexture:  { type: "t", value: myTexture },
};

customMaterial = new THREE.ShaderMaterial( 
{
    uniforms: _uniforms,
    vertexShader: document.getElementById( 'myShader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
    wireframe: false,
    side: THREE.FrontSide 

}   );

document.addEventListener( 'mousemove', onDocumentMouseMove, false );

function onDocumentMouseMove(ev) {
   uniforms.myUniform.value += 0.01; // Updates this new value
   uniforms.myUniform.needsUpdate = true;
}

...

This seemed like a good way to do it for my use case:

const mat = new THREE.ShaderMaterial({
    uniforms: {
        customVec3Uniform: { value: new THREE.Vector3(1,1,1) }
    },
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent,
});

As for updating during runtime (citation):

mat.uniforms.customVec3Uniform.value = new THREE.Vector3(2,2,2);
// mat.needsUpdate = true; // my example works without 'needsUpdate'

For those not using THREE.ShaderMaterial

If for example you are modifying a material beyond just THREE.ShaderMaterial, say THREE.MeshStandardMaterial, via edits to its fragment shader, you will need to do things somewhat differently.

In order to modify this shader's uniforms, WITHOUT requiring recompilation, you will need to store a reference to that shader within the onBeforeCompile callback, then access those uniforms via that stored reference's uniforms.

Typescript class:

First plug into THREE.Material's onBeforeCompile hook transferring the values stored somewhere (in my case a uniforms dictionary created in the constructor) into the shader's uniforms. This is important because shader compilation takes place prior to first usage. Store a reference to the shader in your class for access. Do any of your other work to the fragment shader using these uniforms as you see fit.

private _uniforms: { [uniform: string]: ShaderUniform } = {};
private _shader?:THREE.Shader;

this._material.onBeforeCompile = (shader) => {
    let prepend = "";
    //transfer any changes occurring prior to compilation, 
    //and also prepend these shaders to the fragment shader 
    Object.entries(this._uniforms).forEach(([key, info]) => {
       prepend += `uniform ${info.type} ${key};\n`
       shader.uniforms[key] = {value: info.value};
    });
    //prepend these shaders, along with any other work to the 
    //fragment shader via basic string substitutions 
    shader.fragmentShader = prepend + shader.fragmentShader;
    //store a reference to the shader
    this._shader = shader;
}

Get or set the property from the stored uniforms if not compiled yet, otherwise use the shader's own uniform value, which only exists after compilation.

public getUniform(name:string) : any {
    return this._shader ? this._shader.uniforms[name].value : this._uniforms[name].value;
}

public setUniform(name:string, value:any) {
   if (this._shader) {
       return this._shader.uniforms[name].value = value
   } else {
       this._uniforms[name].value = value;
   }
}
发布评论

评论列表(0)

  1. 暂无评论