如何在three.js中更改自定义着色器的统一值?

时间:2018-04-22 11:17:32

标签: three.js fragment-shader

我已经创建了一个自定义着色器,它显示了带轮廓贴图的顶点的指定值。这是我的着色器的fiddle示例。

 <html lang="en">
<head>
    <title>Face Contour Example</title>

</head>
<body>

<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="http://threejs.org/examples/js/modifiers/SubdivisionModifier.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>

<script type="x-shader/x-vertex" id="vertexShader">

   varying vec3 vColor;
   void main(){
       vColor = color;
       gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
   }

</script>

<script type="x-shader/x-fragment" id="fragmentShader">
varying vec3 vColor;
uniform vec3 uVec3Array[ 6 ];




void main(){

 float r=vColor.r;
 int numberOfColor=6;
 float step=1.0/float(numberOfColor);  

 int index = int(r/step);

 for(int i=0;i<6;i++){
     if(i==index){
         vec3 fragColor= uVec3Array[i];
         gl_FragColor=vec4(fragColor,1.0);
     }
 }

 // gl_FragColor=vec4(vColor,0);


 }
</script>


    <script type="text/javascript">
        var camera, scene, renderer, mesh, material, controls;
        init();
        animate();

        function init() {
            // Renderer.
            renderer = new THREE.WebGLRenderer();
            //renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            // Add renderer to page
            document.body.appendChild(renderer.domElement);

            // Create camera.
            camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
            camera.position.z = 3;

            // Create scene.
            scene = new THREE.Scene();


            var palette1={
                "uVec3Array" : { 
                    type: "v3v", 
                    value: [                                                                         
                            new THREE.Vector3( 0,0,1.0),
                            new THREE.Vector3( 66/255, 238/255, 244/255 ),
                            new THREE.Vector3( 0,1,0 ), 
                            new THREE.Vector3( 1,1,0 ),
                            new THREE.Vector3( 1,0.4,0.2),
                            new THREE.Vector3( 1,0,0 )
                          ] }
            }


            var palette2={
                "uVec3Array" : { 
                    type: "v3v", 
                    value: [                                                                         
                            new THREE.Vector3( 1,0,0 ),
                            new THREE.Vector3( 1,0.4,0.2),
                            new THREE.Vector3( 1,1,0 ),
                            new THREE.Vector3( 0,1,0 ),                             
                            new THREE.Vector3( 66/255, 238/255, 244/255 ),
                            new THREE.Vector3( 0,0,1.0)                            
                          ] }
            }

            var selectedPalette=palette1;

            // var fShader = document.getElementById('smoothPlotFShader').text;
            // var vShader = document.getElementById('vertexShader').text;

            var material;

            var vertexShader=document.getElementById('vertexShader').textContent;
            var fragmentShader= document.getElementById('fragmentShader').textContent ;

            var material = new THREE.ShaderMaterial({
              vertexShader: vertexShader,
              fragmentShader: fragmentShader,
              vertexColors: THREE.VertexColors,
              uniforms:selectedPalette,
              side:2

            });


            // var wireframe = new THREE.MeshLambertMaterial({color: 0x7777ff,wireframe:true});

            var cubeGeometry = new THREE.CubeGeometry( 1,1,1 );
            var modifier = new THREE.SubdivisionModifier( 3 );
            modifier.modify( cubeGeometry ); 
            var cube = new THREE.Mesh( cubeGeometry, material );
            scene.add( cube ); 


            //Create value array linearyly
            var numberOfValues=cubeGeometry.vertices.length;
            var maxValue=30;
            var minValue=-30;
            var values=[]

            for(var i=0;i<numberOfValues;i++){
                values.push(minValue+(maxValue-minValue)*i/numberOfValues);
            }



            applyValuesToMesh(values,cube);

            //Orbit controls
            controls = new THREE.OrbitControls( camera );


            // Create ambient light and add to scene.
            var light = new THREE.AmbientLight(0x404040); // soft white light
            scene.add(light);

            // Create directional light and add to scene.
            var directionalLight = new THREE.DirectionalLight(0xffffff);
            directionalLight.position.set(1, 1, 1).normalize();
            scene.add(directionalLight);

            // Add listener for window resize.
            window.addEventListener('resize', onWindowResize, false);


            function switchPlatte(){
                if(selectedPalette===palette1){
                    selectedPalette=palette2;
                }else{
                    selectedPalette=palette1;
                }

                material.uniforms=selectedPalette;
                material.needsUpdate=true;
                cube.geometry.elementsNeedUpdate = true;

                console.log("Material colors are changed");
            }


            //This button switches plattes of material
            var gui = new dat.GUI();
            var obj = { changeColors:switchPlatte}
            gui.add(obj,'changeColors');

        }

        function animate() {
            requestAnimationFrame(animate);

            controls.update();
            renderer.render(scene, camera);
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        var mesh;


        function applyValuesToMesh(result,mesh){


            var max=findMax(result);
            var min=findMin(result);


            function normalizeValue(value){
                var range=max-min;
                return (value-min)/range;
            }

            var normalizedResult=[];

            for (r in result){
                normalizedResult.push(normalizeValue(result[r]));

            }

            var numberOfNodes=mesh.geometry.vertices.length;
            var faces=mesh.geometry.faces;

            // debugger



            for(f in faces){
                faces[f].vertexColors[0]=new THREE.Color(normalizedResult[faces[f].a],0.5,0.5);
                faces[f].vertexColors[1]=new THREE.Color(normalizedResult[faces[f].b],0.5,0.5);
                faces[f].vertexColors[2]=new THREE.Color(normalizedResult[faces[f].c],0.5,0.5);

            }              


            mesh.geometry.elementsNeedUpdate = true;


        }

        function findMax(array){
            var max=-Infinity;
            for (v in array){
                if(array[v]>max) max=array[v];

            }
            return max;
        }

        function findMin(array){
            var min=Infinity;
            for (v in array){
                if(array[v]<min) min=array[v];

            }
            return min;
        }


    </script>

</body>

代码可能看起来很复杂。基本上,我正在创建一个细分的立方体,并为每个面添加顶点颜色。但是,顶点颜色表示值(值可以是任何值,不需要依赖几何),而不是颜色。如果你看我的着色器,它会提取顶点颜色的.r值,并从输入制服中获取该值的相应颜色。因此,基本上物质制服是在绘制顶点值时将使用的颜色。

我的问题是如果我在创建材料后更改颜色(制服);变化不会影响材料。

在代码的底部,我添加了一个gui按钮,可以在按下时切换材料的颜色(制服)。我已经尝试过材料需求更新,元素需要更新以及文档提供的所有其他更新,但未应用更改。

那么如何在创建材料后更改这些制服。

1 个答案:

答案 0 :(得分:1)

你正在以错误的方式更新制服。只需设置一次制服并仅使用新向量更新value属性。以下小提琴演示了这种方法:http://jsfiddle.net/50u0rwb4/18/

BTW:更新制服时不需要以下声明。

material.needsUpdate = true;
cube.geometry.elementsNeedUpdate = true;