如何防止 THREE.js 着色器中的顶点颜色插值?

How to prevent interpolation of vertex colors in THREE.js shader?

我正在尝试编写一个在网格上绘制等高线图的着色器。

这里是 contour plot 的例子。

我的第一个目标是用不同的颜色想象一个三角形的脸。 您可以在 here 中找到我正在使用的代码。

<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 id="vertexShader" type="x-shader/x-vertex">


        varying vec3 vColor;

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

        <script id="fragmentShader" type="x-shader/x-fragment">
            varying vec3 vColor;

            void main(){
                gl_FragColor = vec4( vColor.rgb, 1.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 = -400;

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

                var colors = {
                  "color1" : {
                    type : "c",
                    value : new THREE.Color(0xff0000) //r
                  },
                  "color2" : {
                    type : "c",
                    value : new THREE.Color(0x00ff00) //b
                  },
                  "color3" : {
                    type : "c",
                    value : new THREE.Color(0x0000ff) //g
                  },
                };

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

                // Create material
                var material = new THREE.ShaderMaterial({
                  vertexShader: vShader,
                  fragmentShader: fShader,
                  vertexColors: THREE.VertexColors,

                });

                // var material  = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } );

                // Create cube and add to scene.
                var geometry = new THREE.Geometry();
                geometry.vertices=[
                    new THREE.Vector3(100,0,0),
                    new THREE.Vector3(-100,0,0),
                    new THREE.Vector3(50,100,0)
                ]

                var face=new THREE.Face3();
                face.a=0;
                face.b=1;
                face.c=2;
                face.vertexColors[ 0 ] = colors["color1"].value;
                face.vertexColors[ 1 ] = colors["color2"].value;
                face.vertexColors[ 2 ] = colors["color3"].value;
                geometry.faces=[face]



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


                function addWireFrame(){
                    //Create wireframe helper for mesh with same geometry
                    var wireframeMesh=new THREE.WireframeGeometry(geometry);
                    var line = new THREE.LineSegments( wireframeMesh );
                    line.material.depthTest = false;
                    line.material.opacity = 0.75;
                    line.material.transparent = true;                            
                    mesh.add( line );
                }
                addWireFrame();

                //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 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);
            }
        </script>

    </body>
</html>

在代码中,我为面的每个顶点分配了红色、绿色和蓝色。

在顶点着色器中,我将这些颜色重定向到片段着色器。在片段着色器中,我计划使用我自己的公式来决定片段实例使用哪种颜色。 (我的公式会根据脸上的位置而定。)

但是,我无法阻止顶点颜色的插值。 three.js?

此外,我很欣赏可能适合我的问题的替代解决方案。

您不想禁用插值。相反,您想要使用插值坐标作为索引。插值颜色值告诉您您与每个顶点的距离。然后,您可以将此插值量化为范围,或将索引量化为颜色数组,以生成最终颜色。

我修改了你的 fiddle 以使用以下像素着色器显示最近顶点的颜色:

void main(){
    vec3 c = vColor;
    gl_FragColor = vec4(c.r > c.g && c.r > c.b ? 1.0 : 0.0,
      c.g > c.r && c.g > c.b ? 1.0 : 0.0,
      c.b > c.r && c.b > c.g ? 1.0 : 0.0,
      1.0 );
}

结果如下所示:

您将需要更复杂的量化方法来显示等高线图,但我希望这种方法能给您一个好的开始。