顶点着色器

时间:2015-06-29 15:27:51

标签: three.js glsl webgl shader vertex-shader

代码演示问题 (注释/取消注释顶点着色器中的gl_Position行)

    var scene;
    var book;
    var shaderMaterial;

    var renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    renderer.setClearColor(0x000000);
    document.body.appendChild(renderer.domElement);

    var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 40000);


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

    scene = new THREE.Scene();

    camera.position.z = 25;
    camera.position.y = 15;
    scene.add(camera);


    var grid = new THREE.GridHelper(100, 10);
    scene.add(grid);


    var controls = new THREE.OrbitControls(camera);
    controls.damping = 0.2;
    var lettersPerSide = 16;

    function createGlpyhSheet() {

        var fontSize = 64;

        var c = document.createElement('canvas');
        c.width = c.height = fontSize * lettersPerSide;
        var ctx = c.getContext('2d');
        ctx.font = fontSize + 'px Monospace';
        var i = 0;

        for (var y = 0; y < lettersPerSide; y++) {
            for (var x = 0; x < lettersPerSide; x++, i++) {
                var ch = String.fromCharCode(i);
                ctx.fillText(ch, x * fontSize, -(8 / 32) * fontSize + (y + 1) * fontSize);
            }
        }

        var tex = new THREE.Texture(c);
        tex.flipY = false;
        tex.needsUpdate = true;

        return tex;
    }


    function createLabels(textArrays, positions) {
        //console.log(textArrays, positions);

        var master_geometry = new THREE.Geometry();


        for (var k = 0; k < textArrays.length; k++) {

            var geo = new THREE.Geometry();
            geo.dynamic = true;

            var str = textArrays[k];
            var vec = positions[k];
            //console.log(shaderMaterial);

            //console.log('str is', str, 'vec is', vec);


            var j = 0,
                ln = 0;

            for (i = 0; i < str.length; i++) {

                //console.log('creating glyph', str[i]);

                var code = str.charCodeAt(i);
                var cx = code % lettersPerSide;
                var cy = Math.floor(code / lettersPerSide);
                var oneDotOne = .55;

                geo.vertices.push(
                new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0).add(vec),
                new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0).add(vec),
                new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0).add(vec),
                new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0).add(vec));
                shaderMaterial.attributes.labelpos.value.push(vec);
                shaderMaterial.attributes.labelpos.value.push(vec);
                shaderMaterial.attributes.labelpos.value.push(vec);
                shaderMaterial.attributes.labelpos.value.push(vec);

                var face = new THREE.Face3(i * 4 + 0, i * 4 + 1, i * 4 + 2);
                geo.faces.push(face);
                face = new THREE.Face3(i * 4 + 0, i * 4 + 2, i * 4 + 3);
                geo.faces.push(face);

                var ox = (cx + 0.05) / lettersPerSide;
                var oy = (cy + 0.05) / lettersPerSide;
                var off = 0.9 / lettersPerSide;

                geo.faceVertexUvs[0].push([
                new THREE.Vector2(ox, oy + off),
                new THREE.Vector2(ox + off, oy + off),
                new THREE.Vector2(ox + off, oy)]);
                geo.faceVertexUvs[0].push([
                new THREE.Vector2(ox, oy + off),
                new THREE.Vector2(ox + off, oy),
                new THREE.Vector2(ox, oy)]);
                if (code == 10) {
                    ln--;
                    j = 0;
                } else {
                    j++;
                }
            }

            // i can only get this working with merge.
            // Building one giant geometry doesn't work for some reason
            master_geometry.merge(geo);

        }

        console.log(shaderMaterial);
        shaderMaterial.attributes.labelpos.needsUpdate = true;

        book = new THREE.Mesh(
        master_geometry,
        shaderMaterial);

        //book.doubleSided = true;
        scene.add(book);

    }


    var uniforms = {
        map: {
            type: "t",
            value: createGlpyhSheet()
        }
    };

    var attributes = {
        labelpos: {
            type: 'v3',
            value: []
        }
    };

    shaderMaterial = new THREE.ShaderMaterial({
        attributes: attributes,
        uniforms: uniforms,
        vertexShader: document.querySelector('#vertex').textContent,
        fragmentShader: document.querySelector('#fragment').textContent
    });
    shaderMaterial.transparent = true;
    shaderMaterial.depthTest = false;


    strings = [];
    vectors = [];
    var sizeOfWorld = 100;
    var halfSize = sizeOfWorld * 0.5;

    for (var i = 0; i < 500; i++) {

        strings.push('test' + i);
        var vector = new THREE.Vector3();
        vector.x = Math.random() * sizeOfWorld - halfSize;
        vector.y = Math.random() * sizeOfWorld - halfSize;
        vector.z = Math.random() * sizeOfWorld - halfSize;
        vectors.push(vector);

    }

    console.log('creating labels');
    createLabels(strings, vectors);

    function animate() {
        controls.update();
        renderer.render(scene, camera);
        requestAnimationFrame(animate, renderer.domElement);
    }

    animate();
        html {
            background-color: #ffffff;
        }
        * {
            margin: 0;
            padding: 0;
        }
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script id="vertex" type="text/x-glsl-vert">
    varying vec2 vUv;
    attribute vec3 labelpos;

    void main() {
        vUv = uv;


        // standard gl_Position.  Labels stay in the correct place, but do not billboard.
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

        // this is the billboarding position as described by:
        // http://stackoverflow.com/questions/22053932/three-js-billboard-vertex-shader
    //gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0) + vec4(position.x, position.y, 0.0, 0.0));

        // this gets a little closer
        //gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, position.z, 1.0) + vec4(position.x, position.y, 0.0, 0.0));

    }
</script>
<script id="fragment" type="text/x-glsl-frag">
    varying vec2 vUv;
    uniform sampler2D map;
    void main() {
        vec4 diffuse = texture2D(map, vUv);
        vec4 letters = mix(diffuse, vec4(1.0, 1.0, 1.0, diffuse.a), 1.0);
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * letters;
    }
</script>

我需要在我的场景中帮助广告标签。我的最后一幕将有数百个标签,我想面对镜头。我无法通过单个网格几何体找到一种方法。我尝试了一些不同的gl_Position方法来获得广告牌效果:

// standard gl_Position.  Labels stay in the correct place, but do not billboard.
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

// this is the billboarding position as described by:
// http://stackoverflow.com/questions/22053932/three-js-billboard-vertex-shader
gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0) + vec4(position.x, position.y, 0.0, 0.0));

// this gets a little closer
gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, position.z, 1.0) + vec4(position.x, position.y, 0.0, 0.0));

我的想法是将着色器属性发送到每个顶点以协助广告牌计算,这就是为什么我在顶点着色器中有label_pos属性。

如果每个标签(由字符组成)分别添加到场景中,我可以获得我想要的精确外观。不幸的是,这导致每个渲染循环的绘制调用太多,因此将它们全部添加到单个几何体中的原因。

非常感谢对此的任何帮助,谢谢。

2 个答案:

答案 0 :(得分:5)

我想你想要

gl_Position = projectionMatrix * 
              (modelViewMatrix * vec4(labelpos, 1) +
               vec4(position.xy, 0, 0));

并且您不需要将位置添加到顶点

geo.vertices.push(
  new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0),
  new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0),
  new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0),
  new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0));

否则你将两次进入该职位。

因为所有标签都在同一个网格中,所以只有1个绘制调用,这意味着您不会为每个标签获取不同的位置,除非您将其传入(您在标签中但是您没有使用它)

在哪种情况下,modelViewMatrix * vec4(0,0,0,1)与仅说modelViewMatrix[3]相同。您所做的只是获取包含所有标签的模型的翻译。如果每个标签都是一个单独的网格并且有自己的矩阵,那么这将起作用,但由于你将它们全部放在一个网格中,它将无法工作。

您的修正是在您已经包含的单独属性中传递每个标签的位置,您只需要使用它。

modelViewMatrix * vec4(labelpos, 1)

获取标签的根

vec4(position.x, position.y, 0.0, 0.0)

添加视图空间中的角落

&#13;
&#13;
var scene;
    var book;
    var shaderMaterial;

    var renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    renderer.setClearColor(0x000000);
    document.body.appendChild(renderer.domElement);

    var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 40000);


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

    scene = new THREE.Scene();

    camera.position.z = 25;
    camera.position.y = 15;
    scene.add(camera);


    var grid = new THREE.GridHelper(100, 10);
    scene.add(grid);


    var controls = new THREE.OrbitControls(camera);
    controls.damping = 0.2;
    var lettersPerSide = 16;

    function createGlpyhSheet() {

        var fontSize = 64;

        var c = document.createElement('canvas');
        c.width = c.height = fontSize * lettersPerSide;
        var ctx = c.getContext('2d');
        ctx.font = fontSize + 'px Monospace';
        var i = 0;

        for (var y = 0; y < lettersPerSide; y++) {
            for (var x = 0; x < lettersPerSide; x++, i++) {
                var ch = String.fromCharCode(i);
                ctx.fillText(ch, x * fontSize, -(8 / 32) * fontSize + (y + 1) * fontSize);
            }
        }

        var tex = new THREE.Texture(c);
        tex.flipY = false;
        tex.needsUpdate = true;

        return tex;
    }


    function createLabels(textArrays, positions) {
        //console.log(textArrays, positions);

        var master_geometry = new THREE.Geometry();


        for (var k = 0; k < textArrays.length; k++) {

            var geo = new THREE.Geometry();
            geo.dynamic = true;

            var str = textArrays[k];
            var vec = positions[k];
            //console.log(shaderMaterial);

            //console.log('str is', str, 'vec is', vec);


            var j = 0,
                ln = 0;

            for (i = 0; i < str.length; i++) {

                //console.log('creating glyph', str[i]);

                var code = str.charCodeAt(i);
                var cx = code % lettersPerSide;
                var cy = Math.floor(code / lettersPerSide);
                var oneDotOne = .55;

                geo.vertices.push(
                new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0),
                new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0),
                new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0),
                new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0));
                shaderMaterial.attributes.labelpos.value.push(vec);
                shaderMaterial.attributes.labelpos.value.push(vec);
                shaderMaterial.attributes.labelpos.value.push(vec);
                shaderMaterial.attributes.labelpos.value.push(vec);

                var face = new THREE.Face3(i * 4 + 0, i * 4 + 1, i * 4 + 2);
                geo.faces.push(face);
                face = new THREE.Face3(i * 4 + 0, i * 4 + 2, i * 4 + 3);
                geo.faces.push(face);

                var ox = (cx + 0.05) / lettersPerSide;
                var oy = (cy + 0.05) / lettersPerSide;
                var off = 0.9 / lettersPerSide;

                geo.faceVertexUvs[0].push([
                new THREE.Vector2(ox, oy + off),
                new THREE.Vector2(ox + off, oy + off),
                new THREE.Vector2(ox + off, oy)]);
                geo.faceVertexUvs[0].push([
                new THREE.Vector2(ox, oy + off),
                new THREE.Vector2(ox + off, oy),
                new THREE.Vector2(ox, oy)]);
                if (code == 10) {
                    ln--;
                    j = 0;
                } else {
                    j++;
                }
            }

            // i can only get this working with merge.
            // Building one giant geometry doesn't work for some reason
            master_geometry.merge(geo);

        }

        console.log(shaderMaterial);
        shaderMaterial.attributes.labelpos.needsUpdate = true;

        book = new THREE.Mesh(
        master_geometry,
        shaderMaterial);

        //book.doubleSided = true;
        scene.add(book);

    }


    var uniforms = {
        map: {
            type: "t",
            value: createGlpyhSheet()
        }
    };

    var attributes = {
        labelpos: {
            type: 'v3',
            value: []
        }
    };

    shaderMaterial = new THREE.ShaderMaterial({
        attributes: attributes,
        uniforms: uniforms,
        vertexShader: document.querySelector('#vertex').textContent,
        fragmentShader: document.querySelector('#fragment').textContent
    });
    shaderMaterial.transparent = true;
    shaderMaterial.depthTest = false;


    strings = [];
    vectors = [];
    var sizeOfWorld = 100;
    var halfSize = sizeOfWorld * 0.5;

    for (var i = 0; i < 500; i++) {

        strings.push('test' + i);
        var vector = new THREE.Vector3();
        vector.x = Math.random() * sizeOfWorld - halfSize;
        vector.y = Math.random() * sizeOfWorld - halfSize;
        vector.z = Math.random() * sizeOfWorld - halfSize;
        vectors.push(vector);

    }

    console.log('creating labels');
    createLabels(strings, vectors);

    function animate() {
        controls.update();
        renderer.render(scene, camera);
        requestAnimationFrame(animate, renderer.domElement);
    }

    animate();
&#13;
html {
            background-color: #ffffff;
        }
        * {
            margin: 0;
            padding: 0;
        }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/69/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/4862f5f1111346a957ac3e0cb0858be1568d0e03/examples/js/controls/OrbitControls.js"></script>
<script id="vertex" type="text/x-glsl-vert">
    varying vec2 vUv;
    attribute vec3 labelpos;

    void main() {
        vUv = uv;


        gl_Position = projectionMatrix * 
                      (modelViewMatrix * vec4(labelpos, 1) +
                       vec4(position.xy, 0, 0));

    }
</script>
<script id="fragment" type="text/x-glsl-frag">
    varying vec2 vUv;
    uniform sampler2D map;
    void main() {
        vec4 diffuse = texture2D(map, vUv);
        vec4 letters = mix(diffuse, vec4(1.0, 1.0, 1.0, diffuse.a), 1.0);
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * letters;
    }
</script>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

还值得研究在Three.js及其SpriteMaterial中如何完成:sprite_vert.glsl

这是一个带注释的代码段:

// optional: pass 2D rotation angle as an uniform
uniform float rotation;
// optional: pass 2D center point as an uniform
uniform vec2 center;

// optional: use this define to scale the model according to distance from the camera
#define USE_SIZEATTENUATION

// [skipped includes]

void main() {
    // discard rotation and scale
    vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );

    // extract model's scale
    vec2 scale;
    scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
    scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );

    // if not defined, keep model the same size regardless of distance from the camera
    #ifndef USE_SIZEATTENUATION
        bool isPerspective = isPerspectiveMatrix( projectionMatrix );
        if ( isPerspective ) scale *= - mvPosition.z;
    #endif

    // if center is not passed as uniform, create vec2 center = vec2(0.0);

    // aligned with the camera [and scaled]
    vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;

    // if rotation is not passed as uniform, skip the next block

    // rotate 2D
    vec2 rotatedPosition;
    rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
    rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;

    // billboard
    mvPosition.xy += rotatedPosition;

    gl_Position = projectionMatrix * mvPosition;

    // [skipped includes]
}