在WebGL中 - 如何通过更改shaderProgram同时获得纹理和颜色

时间:2013-07-03 02:11:02

标签: colors webgl shader textures

我有另一个问题,WebGL - 学习曲线很陡,我道歉:( - 无论如何,所以我试图有一个显示彩色金字塔和纹理立方体的程序。我使用了基于Nehe的OpenGL教程的WebGL教程并且基本上尝试将教程4和5结合在一起,看看如果我只使用着色器程序的东西,它会如何工作,但它由于某种原因不起作用...任何想法?另外,我想知道是否有人可以让我知道掌握WebGL需要多长时间?我已经掌握了很多基本概念,但是在彻底学习它的过程中,它似乎是如此陡峭的山峰......

<html><head>
    <title>Learning WebGL Lesson 5</title>


    <script type="text/javascript" src="glMatrix-0.9.5.min.js"></script>
    <script type="text/javascript" src="webgl-utils.js"></script>

    <script id="shader-fs" type="x-shader/x-fragment">
        precision mediump float;

        varying vec2 vTextureCoord;

        uniform sampler2D uSampler;

        void main(void) {
            gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        }
        </script>

    <script id="shader-vs" type="x-shader/x-vertex">
        attribute vec3 aVertexPosition;
        attribute vec2 aTextureCoord;

        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;

        varying vec2 vTextureCoord;


        void main(void) {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            vTextureCoord = aTextureCoord;
        }
        </script>




    <script id="shader-fcol" type="x-shader/x-fragment">
        precision mediump float;

        varying vec4 vColor;

        void main(void) {
            gl_FragColor = vColor;
        }
        </script>

    <script id="shader-vcol" type="x-shader/x-vertex">
        attribute vec3 aVertexPosition;
        attribute vec4 aVertexColor;

        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;

        varying vec4 vColor;

        void main(void) {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            vColor = aVertexColor;
        }
        </script>














    <script type="text/javascript">

        var gl;

        function initGL(canvas) {
            try {
                gl = canvas.getContext("experimental-webgl");
                gl.viewportWidth = canvas.width;
                gl.viewportHeight = canvas.height;
            } catch (e) {
            }
            if (!gl) {
                alert("Could not initialise WebGL, sorry :-(");
            }
        }


        function getShader(gl, id) {
            var shaderScript = document.getElementById(id);
            if (!shaderScript) {
                return null;
            }

            var str = "";
            var k = shaderScript.firstChild;
            while (k) {
                if (k.nodeType == 3) {
                    str += k.textContent;
                }
                k = k.nextSibling;
            }

            var shader;
            if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
            } else if (shaderScript.type == "x-shader/x-vertex") {
                shader = gl.createShader(gl.VERTEX_SHADER);
            } else {
                return null;
            }

            gl.shaderSource(shader, str);
            gl.compileShader(shader);

            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                alert(gl.getShaderInfoLog(shader));
                return null;
            }

            return shader;
        }





        var shaderProgram;
        var shaderProgram2;

        function initShaders() {
            var fragmentShader = getShader(gl, "shader-fs");
            var vertexShader = getShader(gl, "shader-vs");

            shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);

            if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                alert("Could not initialise shaders");
            }

           // gl.useProgram(shaderProgram);

            shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
            gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

            shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
            gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);

            shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
            shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
            shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");






            ////////////

            var colorShader = getShader(gl, "shader-fcol");
            var vertcolorShader = getShader(gl, "shader-vcol");

            shaderProgram2 = gl.createProgram();
            gl.attachShader(shaderProgram2, vertcolorShader);
            gl.attachShader(shaderProgram2, colorShader);
            gl.linkProgram(shaderProgram2);

            if (!gl.getProgramParameter(shaderProgram2, gl.LINK_STATUS)) {
                alert("Could not initialise shaders");
            }

            gl.useProgram(shaderProgram2);

            shaderProgram2.vertexPositionAttribute = gl.getAttribLocation(shaderProgram2, "aVertexPosition");
            gl.enableVertexAttribArray(shaderProgram2.vertexPositionAttribute);

            shaderProgram2.vertexColorAttribute = gl.getAttribLocation(shaderProgram2, "aVertexColor");
            gl.enableVertexAttribArray(shaderProgram2.vertexColorAttribute);

            shaderProgram2.pMatrixUniform = gl.getUniformLocation(shaderProgram2, "uPMatrix");
            shaderProgram2.mvMatrixUniform = gl.getUniformLocation(shaderProgram2, "uMVMatrix");






        }


        function handleLoadedTexture(texture) {
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.bindTexture(gl.TEXTURE_2D, null);
        }


        var neheTexture;

        function initTexture() {
            neheTexture = gl.createTexture();
            neheTexture.image = new Image();
            neheTexture.image.onload = function () {
                handleLoadedTexture(neheTexture)
            }

            neheTexture.image.src = "nehe.gif";
        }


        var mvMatrix = mat4.create();
        var mvMatrixStack = [];
        var pMatrix = mat4.create();

        function mvPushMatrix() {
            var copy = mat4.create();
            mat4.set(mvMatrix, copy);
            mvMatrixStack.push(copy);
        }

        function mvPopMatrix() {
            if (mvMatrixStack.length == 0) {
                throw "Invalid popMatrix!";
            }
            mvMatrix = mvMatrixStack.pop();
        }


        function setMatrixUniforms() {
            gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
            gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
        }


        function degToRad(degrees) {
            return degrees * Math.PI / 180;
        }

        var cubeVertexPositionBuffer;
        var cubeVertexTextureCoordBuffer;
        var cubeVertexIndexBuffer;

        function initBuffers() {
            cubeVertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
            vertices = [
                        // Front face
                        -1.0, -1.0,  1.0,
                        1.0, -1.0,  1.0,
                        1.0,  1.0,  1.0,
                        -1.0,  1.0,  1.0,

                        // Back face
                        -1.0, -1.0, -1.0,
                        -1.0,  1.0, -1.0,
                        1.0,  1.0, -1.0,
                        1.0, -1.0, -1.0,

                        // Top face
                        -1.0,  1.0, -1.0,
                        -1.0,  1.0,  1.0,
                        1.0,  1.0,  1.0,
                        1.0,  1.0, -1.0,

                        // Bottom face
                        -1.0, -1.0, -1.0,
                        1.0, -1.0, -1.0,
                        1.0, -1.0,  1.0,
                        -1.0, -1.0,  1.0,

                        // Right face
                        1.0, -1.0, -1.0,
                        1.0,  1.0, -1.0,
                        1.0,  1.0,  1.0,
                        1.0, -1.0,  1.0,

                        // Left face
                        -1.0, -1.0, -1.0,
                        -1.0, -1.0,  1.0,
                        -1.0,  1.0,  1.0,
                        -1.0,  1.0, -1.0,
                        ];
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
            cubeVertexPositionBuffer.itemSize = 3;
            cubeVertexPositionBuffer.numItems = 24;

            cubeVertexTextureCoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
            var textureCoords = [
                                 // Front face
                                 0.0, 0.0,
                                 1.0, 0.0,
                                 1.0, 1.0,
                                 0.0, 1.0,

                                 // Back face
                                 1.0, 0.0,
                                 1.0, 1.0,
                                 0.0, 1.0,
                                 0.0, 0.0,

                                 // Top face
                                 0.0, 1.0,
                                 0.0, 0.0,
                                 1.0, 0.0,
                                 1.0, 1.0,

                                 // Bottom face
                                 1.0, 1.0,
                                 0.0, 1.0,
                                 0.0, 0.0,
                                 1.0, 0.0,

                                 // Right face
                                 1.0, 0.0,
                                 1.0, 1.0,
                                 0.0, 1.0,
                                 0.0, 0.0,

                                 // Left face
                                 0.0, 0.0,
                                 1.0, 0.0,
                                 1.0, 1.0,
                                 0.0, 1.0,
                                 ];
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
            cubeVertexTextureCoordBuffer.itemSize = 2;
            cubeVertexTextureCoordBuffer.numItems = 24;

            cubeVertexIndexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
            var cubeVertexIndices = [
                                     0, 1, 2,      0, 2, 3,    // Front face
                                     4, 5, 6,      4, 6, 7,    // Back face
                                     8, 9, 10,     8, 10, 11,  // Top face
                                     12, 13, 14,   12, 14, 15, // Bottom face
                                     16, 17, 18,   16, 18, 19, // Right face
                                     20, 21, 22,   20, 22, 23  // Left face
                                     ];
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
            cubeVertexIndexBuffer.itemSize = 1;
            cubeVertexIndexBuffer.numItems = 36;







            ///////////////


            pyramidVertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer);
            var vertices = [
                            // Front face
                            0.0,  1.0,  0.0,
                            -1.0, -1.0,  1.0,
                            1.0, -1.0,  1.0,

                            // Right face
                            0.0,  1.0,  0.0,
                            1.0, -1.0,  1.0,
                            1.0, -1.0, -1.0,

                            // Back face
                            0.0,  1.0,  0.0,
                            1.0, -1.0, -1.0,
                            -1.0, -1.0, -1.0,

                            // Left face
                            0.0,  1.0,  0.0,
                            -1.0, -1.0, -1.0,
                            -1.0, -1.0,  1.0
                            ];
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
            pyramidVertexPositionBuffer.itemSize = 3;
            pyramidVertexPositionBuffer.numItems = 12;

            pyramidVertexColorBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
            var colors = [
                          // Front face
                          1.0, 0.0, 0.0, 1.0,
                          0.0, 1.0, 0.0, 1.0,
                          0.0, 0.0, 1.0, 1.0,

                          // Right face
                          1.0, 0.0, 0.0, 1.0,
                          0.0, 0.0, 1.0, 1.0,
                          0.0, 1.0, 0.0, 1.0,

                          // Back face
                          1.0, 0.0, 0.0, 1.0,
                          0.0, 1.0, 0.0, 1.0,
                          0.0, 0.0, 1.0, 1.0,

                          // Left face
                          1.0, 0.0, 0.0, 1.0,
                          0.0, 0.0, 1.0, 1.0,
                          0.0, 1.0, 0.0, 1.0
                          ];
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
            pyramidVertexColorBuffer.itemSize = 4;
            pyramidVertexColorBuffer.numItems = 12;



















        }


        var xRot = 0;
        var yRot = 0;
        var zRot = 0;

        function drawScene() {



            gl.useProgram(shaderProgram2);

            gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

            mat4.identity(mvMatrix);

            mat4.translate(mvMatrix, [-3.7, 0.0, -15.0]);

            mvPushMatrix();
            mat4.rotate(mvMatrix, degToRad(rPyramid), [0, 1, 0]);

            gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram2.vertexPositionAttribute, pyramidVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

            gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
            gl.vertexAttribPointer(shaderProgram2.vertexColorAttribute, pyramidVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

            setMatrixUniforms();
            gl.drawArrays(gl.TRIANGLES, 0, pyramidVertexPositionBuffer.numItems);

            mvPopMatrix();





            gl.useProgram(shaderProgram);



            gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

            mat4.identity(mvMatrix);

            mat4.translate(mvMatrix, [2.7, 0.0, -15.0]);

            mat4.rotate(mvMatrix, degToRad(xRot), [1, 0, 0]);
            mat4.rotate(mvMatrix, degToRad(yRot), [0, 1, 0]);
            mat4.rotate(mvMatrix, degToRad(zRot), [0, 0, 1]);

            gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

            gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
            gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, neheTexture);
            gl.uniform1i(shaderProgram.samplerUniform, 0);

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
            setMatrixUniforms();
            gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);




                    }


        var lastTime = 0;

        function animate() {
            var timeNow = new Date().getTime();
            if (lastTime != 0) {
                var elapsed = timeNow - lastTime;

                xRot += (90 * elapsed) / 1000.0;
                yRot += (90 * elapsed) / 1000.0;
                zRot += (90 * elapsed) / 1000.0;
            }
            lastTime = timeNow;
        }


        function tick() {
            requestAnimFrame(tick);
            drawScene();
            animate();
        }


        function webGLStart() {
            var canvas = document.getElementById("lesson05-canvas");
            initGL(canvas);
            initShaders();
            initBuffers();
            initTexture();

            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.enable(gl.DEPTH_TEST);

            tick();
        }


        </script>



</head>

<body onload="webGLStart();">

            <canvas id="lesson05-canvas" style="border: none;" width="500" height="500"></canvas>


    </body></html>

2 个答案:

答案 0 :(得分:1)

一种技术是打开/关闭属性并使用1像素的白色纹理。例如,给定一个着色器,用户同时使用纹理和顶点颜色

顶点着色器

attribute vec4 a_position;
attribute vec2 a_texcoord;
attribute vec4 a_color;
uniform mat4 u_worldViewProjection;
varying vec2 v_texcoord;
varying vec4 v_color;

void main() {
  gl_Position = u_worldviewProjection * a_position;
  v_color = a_color;
  v_texcoord = a_texcoord;
}

片段着色器

precision mediump float;
varying vec2 v_texcoord;
varying vec4 v_color;
uniform sampler2D u_texture;

void main() {
    gl_FragColor = texture2D(u_texture, v_texcoord) * v_color;
}

假设你查找这样的位置

var colorLoc = gl.getAttribLocation("a_color");
var texcoordLoc = gl.getAttribLocatoin("a_texcoord");

然后,对于仅使用纹理的模型,请关闭颜色属性并将其设置为白色

gl.disableVertexAttribArray(colorLoc);
gl.vertexAttrib4f(colorLoc, 1, 1, 1, 1);

对于仅使用顶点颜色的模型,请关闭纹理坐标属性并使用1x1像素白色纹理。

// at init time make a 1x1 white texture.
whiteTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, whiteTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
              new Uint8Array([1, 1, 1, 1]));


// at draw time...
// bind the white texture
gl.bindTexture(gl.TEXTURE_2D, whiteTex);
// turn off texcoords.
gl.disableVertexAttribArray(texcoordLoc);

这种技术通常用于游戏,因为切换着色器程序很慢。

答案 1 :(得分:-1)

可以同时使用一个着色器,但编码量(空/冗余缓冲区)和性能问题(GPU在分支上非常糟糕)随之而来是不值得的,所以实际上没有。为了使两者都在同一场景中,建议为每个渲染过程使用专门的着色​​器。你会在Lesson 13中看到这个。

关于掌握多少时间,嗯......这取决于你最终想要用它做什么。如果您只是想制作一些更简单的东西,比如3D模型的陈列室或图像和视频的一些漂亮的效果,那么您可以通过跳过核心并使用three.js来节省大量时间;但是如果你想编写自己的引擎,那么还有很多工作要做,而不仅仅是在缓冲区中翻转以在屏幕上显示内容。

虽然更强大,javascript也需要更多的创造性思维,但在继续之前,您可能需要查看Douglas Crockford about javascript