Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

three.js - Shader for solid color for each triangle of plane geometry

I have been learning webgl from webglfundamentals where I came across a simple shader example where you can paint triangles with solid color. Here is link to tutorial and original demo.

I tried to create same effect in three js using plane geometry but I can't manage achieve solid color shader. When I use almost same setup in Three js, I get more like gradient effect. What am I doing wrong here? (I am noticing that my shader isn't consistent either as it renders differently on refresh) Also, is there place to learn shaders specifically for three js?

    var vShader = ` 
    precision mediump float;
    precision mediump int;
    
    attribute vec4 a_color;
    varying vec4 vColor;
    
    void main()    {
    
      
    
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      vColor = a_color;
    }`;
    
    var fShader = ` precision mediump float;
    precision mediump int;
    
    varying vec4 vColor;
    
    void main()    {
    
      vec4 color = vec4( vColor );
      gl_FragColor = vColor;
    
    }`;
    
    var row = 1;
    var col = 1;
    var w = 600;
    var h = 400;
    
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 100);
    renderer = new THREE.WebGLRenderer();
    camera.position.z = 5;
    
    var viewSize = getViewSize(camera);
    document.body.appendChild(renderer.domElement);
    renderer.setSize(w, h);
    
    
    
    var geometry = new THREE.PlaneBufferGeometry(viewSize.width, viewSize.height, col, row);
    
    
    var color = new THREE.Color();
    const blossomPalette = [0xff0000, 0xff0000, 0xff0000, 0x0000ff, 0x0000ff, 0x0000ff];
    var colors = new Float32Array(4 * 2 * 3 * col * row);
    for (let i = 0; i < 4; i++) {
        color.setHex(blossomPalette[Math.floor(Math.random() * blossomPalette.length)]);
        color.toArray(colors, i * 3);
    }
    geometry.setAttribute('a_color', new THREE.BufferAttribute(colors, 4, false));
    
    var material = new THREE.ShaderMaterial({
        vertexShader: vShader,
        fragmentShader: fShader,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthTest: false,
        vertexColors: true,
        flatShading: true
    });
    
    var plane = new THREE.Mesh(geometry, material);
    
    scene.add(plane);
    
    function animate() {
        renderer.render(scene, camera);
        requestAnimationFrame(animate)
    
    }
    animate();
    
    
    function getViewSize(camera) {
        var fovInRadians = (camera.fov * Math.PI) / 180;
        var height = Math.abs(camera.position.z * Math.tan(fovInRadians / 2) * 2);
        return {
            width: height * camera.aspect,
            height: height
        }
    }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.min.js"></script>
question from:https://stackoverflow.com/questions/65935536/shader-for-solid-color-for-each-triangle-of-plane-geometry

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The PlaneBufferGeometry in three.js is using indexed vertices to share vertices so there are only 4 vertices and then 6 indices to use those 4 vertices to make 2 triangles. That means you can't give each triangle different solid colors because they share 2 vertices and a vertex can only have 1 color.

Further, the code is choosing random colors for each vertex so even if you you used 6 vertices so that the 2 triangles didn't share any you still wouldn't get the result you linked to, instead you'd get this result which is further down the page on the same tutorial.

Finally the code is only generating 3 floats per color so you need to set the number of components for the color attribute to 3 instead of 4

If you want to repeat the webgl sample you'll need to provide your own 6 vertices.

var vShader = ` 
    precision mediump float;
    precision mediump int;
    
    attribute vec4 a_color;
    varying vec4 vColor;
    
    void main()    {
    
      
    
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      vColor = a_color;
    }`;
    
    var fShader = ` precision mediump float;
    precision mediump int;
    
    varying vec4 vColor;
    
    void main()    {
    
      vec4 color = vec4( vColor );
      gl_FragColor = vColor;
    
    }`;
    
    var row = 1;
    var col = 1;
    var w = 600;
    var h = 400;
    
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 100);
    renderer = new THREE.WebGLRenderer();
    camera.position.z = 5;
    
    var viewSize = getViewSize(camera);
    document.body.appendChild(renderer.domElement);
    renderer.setSize(w, h);
    
    
    
    var geometry = new THREE.BufferGeometry();
    var x = viewSize.width / 2;
    var y = viewSize.height / 2;
    var positions = new Float32Array([
       -x, -y, 0,
        x, -y, 0,
       -x,  y, 0,
       -x,  y, 0,
        x, -y, 0,
        x,  y, 0,
    ]);
    geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3, false));
    
    var color = new THREE.Color();
    const blossomPalette = [
       0xff0000, 0xff0000, 0xff0000,
       0x0000ff, 0x0000ff, 0x0000ff,
     ];
    var colors = new Float32Array(2 * 3 * 3 * col * row);
    for (let i = 0; i < 6; i++) {
        color.setHex(blossomPalette[i]);
        color.toArray(colors, i * 3);
    }
    geometry.setAttribute('a_color', new THREE.BufferAttribute(colors, 3, false));
    
    var material = new THREE.ShaderMaterial({
        vertexShader: vShader,
        fragmentShader: fShader,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthTest: false,
        vertexColors: true,
        flatShading: true
    });
    
    var plane = new THREE.Mesh(geometry, material);
    
    scene.add(plane);
    
    function animate() {
        renderer.render(scene, camera);
        requestAnimationFrame(animate)
    
    }
    animate();
    
    
    function getViewSize(camera) {
        var fovInRadians = (camera.fov * Math.PI) / 180;
        var height = Math.abs(camera.position.z * Math.tan(fovInRadians / 2) * 2);
        return {
            width: height * camera.aspect,
            height: height
        }
    }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.min.js"></script>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...