【问题标题】:WebGL: cannot display two texturesWebGL:无法显示两个纹理
【发布时间】:2019-11-04 18:14:59
【问题描述】:

我正在尝试渲染一个带有纹理的天空盒(使用立方体贴图)的纹理立方体(使用顶点、索引和 tex 坐标),但不知何故我总是收到以下错误消息:

WebGL:INVALID_OPERATION:bindTexture:纹理不能用于多个目标

我有两个纹理并且可能错误地使用了 gl.activeTexture 但我无法弄清楚。

如您所见,带纹理的立方体在天空盒似乎被绘制之前短暂闪烁。

使用此代码的临时(24 小时)网站:http://priceless-dijkstra-4bf2a5.netlify.com/

有什么想法吗?

<!-- Licensed under a BSD license. See license.html for license -->
<!-- src: https://webglfundamentals.org/ -->
<!DOCTYPE html>
<html>
    <head>
        <meta charset = "utf-8">
        <meta name = "viewport" content = "width=device-width, initial-scale=1.0, user-scalable=yes">
        <title> WebGL - Textures - Data Texture 3 x2</title>
        <link type = "text/css" href = "./webgl-tutorials.css" rel = "stylesheet" />
    </head>
    <body>
        <div class = "description">
            A 3 x2 texture <br />
        </div>
        <canvas id = "canvas"></canvas>
    </body>
    <!-- vertex shader -->
    <script id = "3d-vertex-shader" type = "x-shader/x-vertex">
        attribute vec4 a_position;
        attribute vec2 a_texcoord;
        uniform mat4 u_matrix;
        varying vec2 v_texcoord;
        void main()
        {
            // Multiply the position by the matrix.
            gl_Position = u_matrix * a_position;
            // Pass the texcoord to the fragment shader.
            v_texcoord = a_texcoord;
        }
    </script>
    <!-- fragment shader -->
    <script id = "3d-fragment-shader" type = "x-shader/x-fragment">
        precision mediump float;
        // Passed in from the vertex shader.
        varying vec2 v_texcoord;
        // The texture.
        uniform sampler2D u_texture;
        void main()
        {
            gl_FragColor = texture2D(u_texture, v_texcoord);
        }
    </script>
    <!--skybox vertex shader-->
    <script id="skybox-vertex-shader" type="x-shader/x-vertex">
        attribute vec4 a_position;
        varying vec4 v_position;
        void main() 
        {
          v_position = a_position;
          gl_Position = a_position;
        }
    </script>
    <!--skybox fragment shader-->
    <script id="skybox-fragment-shader" type="x-shader/x-fragment">
        precision mediump float;
        uniform samplerCube u_skybox;
        uniform mat4 u_viewDirectionProjectionInverse;
        varying vec4 v_position;
        void main() 
        {
          vec4 t = u_viewDirectionProjectionInverse * v_position;
          gl_FragColor = textureCube(u_skybox, normalize(t.xyz / t.w));
        }
    </script>
    <script src = "./webgl-utils.js"></script>
    <script src = "./m4.js"></script>
    <script src = "./primitives.js"></script>
    <script type = "module">
        "use strict";
        function main()
        {
            // Get A WebGL context
            /** @type {HTMLCanvasElement} */
            var canvas = document.getElementById("canvas");
            var gl = canvas.getContext("webgl");
            if (!gl)
            {
                return;
            }
            // setup GLSL program
            var program = webglUtils.createProgramFromScripts(gl, ["3d-vertex-shader", "3d-fragment-shader"]);
            // look up where the vertex data needs to go.
            var positionLocation = gl.getAttribLocation(program, "a_position");
            var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");
            // lookup uniforms
            var matrixLocation = gl.getUniformLocation(program, "u_matrix");
            var textureLocation = gl.getUniformLocation(program, "u_texture");

            //create program for skybox
            const skyboxProgramInfo = webglUtils.createProgramInfo(gl, ["skybox-vertex-shader", "skybox-fragment-shader"]);
            var sb_textureLocation = gl.getUniformLocation(skyboxProgramInfo.program, "u_skybox");
            // create buffers and fill with vertex data
            const cubeBufferInfo = primitives.createCubeBufferInfo(gl, 1);
            const quadBufferInfo = primitives.createXYQuadBufferInfo(gl);
            // Create a texture.
            const sb_texture = gl.createTexture();
            gl.activeTexture(gl.TEXTURE0 + 1);
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, sb_texture);
            const faceInfos = 
            [
                { target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, url: './pos-x.jpg', },
                { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, url: './neg-x.jpg', },
                { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, url: './pos-y.jpg', },
                { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, url: './neg-y.jpg', },
                { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, url: './pos-z.jpg', },
                { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, url: './neg-z.jpg', },
            ];
            faceInfos.forEach((faceInfo) => 
            {
                const {target, url} = faceInfo;
                // Upload the canvas to the cubemap face.
                const level = 0;
                const internalFormat = gl.RGBA;
                const width = 512;
                const height = 512;
                const format = gl.RGBA;
                const type = gl.UNSIGNED_BYTE;
                // setup each face so it's immediately renderable
                gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null);
                // Asynchronously load an image
                const image = new Image();
                image.src = url;
                image.addEventListener('load', function() 
                {
                    // Now that the image has loaded make copy it to the skybox texture.
                    gl.activeTexture(gl.TEXTURE0 + 1);
                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, sb_texture);
                    gl.texImage2D(target, level, internalFormat, format, type, image);
                    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                });
            });
            gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);


            // Create a buffer for positions
            var positionBuffer = gl.createBuffer();
            // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
            gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
            // Put the positions in the buffer
            setGeometry(gl);

            // Create a buffer for positions
            var indexBuffer = gl.createBuffer();
            // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
            // Put the positions in the buffer
            setIndices(gl);

            // provide texture coordinates for the rectangle.
            var texcoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
            // Set Texcoords.
            setTexcoords(gl);
            // Create a texture.
            var texture = gl.createTexture();
            //void gl.bindTexture(target, texture);
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture);
            // fill texture with 3x2 pixels
            const level = 0;
            const internalFormat = gl.RGB;
            const width = 2;                
            const height = 2;               
            const border = 0;
            const format = gl.RGB;          
            const type = gl.UNSIGNED_BYTE;
            const data = new Uint8Array     
            ([
                255, 0, 0,      0, 255, 0,
                0, 0, 255,      128, 128, 128,
            ]);
            const alignment = 1;
            gl.pixelStorei(gl.UNPACK_ALIGNMENT, alignment);
            gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, format, type, data);
            // set the filtering so we don't need mips and it's not filtered
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

            function degToRad(d)
            {
                return d * Math.PI / 180;
            }
            var fieldOfViewRadians = degToRad(60);
            var modelXRotationRadians = degToRad(0);
            var modelYRotationRadians = degToRad(0);
            // Get the starting time.
            var then = 0;
            requestAnimationFrame(drawScene);
            // Draw the scene.
            function drawScene(time)
            {
                // convert to seconds
                time *= 0.001;
                // Subtract the previous time from the current time
                var deltaTime = time - then;
                // Remember the current time for the next frame.
                then = time;
                webglUtils.resizeCanvasToDisplaySize(gl.canvas);
                // Tell WebGL how to convert from clip space to pixels
                gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
                gl.enable(gl.CULL_FACE);
                gl.enable(gl.DEPTH_TEST);
                // Animate the rotation
                modelYRotationRadians += -0.7 * deltaTime;
                modelXRotationRadians += -0.4 * deltaTime;
                // Clear the canvas AND the depth buffer.
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                // Tell it to use our program (pair of shaders)
                gl.useProgram(program);

                // Turn on the position attribute
                gl.enableVertexAttribArray(positionLocation);
                // Bind the position buffer.
                gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
                // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
                var size = 3; // 3 components per iteration
                var type = gl.FLOAT; // the data is 32bit floats
                var normalize = false; // don't normalize the data
                var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
                var offset = 0; // start at the beginning of the buffer
                gl.vertexAttribPointer( positionLocation, size, type, normalize, stride, offset);

                // Turn on the teccord attribute
                gl.enableVertexAttribArray(texcoordLocation);
                // Bind the position buffer.
                gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
                // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
                var size = 2; // 2 components per iteration
                var type = gl.FLOAT; // the data is 32bit floats
                var normalize = false; // don't normalize the data
                var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
                var offset = 0; // start at the beginning of the buffer
                gl.vertexAttribPointer( texcoordLocation, size, type, normalize, stride, offset);

                // Compute the projection matrix
                var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
                var projectionMatrix = m4.perspective(fieldOfViewRadians, aspect, 1, 2000);
                var cameraPosition = [0, 0, 2];
                var up = [0, 1, 0];
                var target = [0, 0, 0];
                // Compute the camera's matrix using look at.
                var cameraMatrix = m4.lookAt(cameraPosition, target, up);
                // Make a view matrix from the camera matrix.
                var viewMatrix = m4.inverse(cameraMatrix);
                var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);
                var matrix = m4.xRotate(viewProjectionMatrix, modelXRotationRadians);
                matrix = m4.yRotate(matrix, modelYRotationRadians);
                // Set the matrix.
                gl.uniformMatrix4fv(matrixLocation, false, matrix);
                // Tell the shader to use texture unit 0 for u_texture
                gl.uniform1i(textureLocation, 0);
                // Draw the geometry.
                gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);

                // Skybox: we only care about direction so remove the translation
                var viewDirectionMatrix = m4.copy(viewMatrix);
                viewDirectionMatrix[12] = 0;
                viewDirectionMatrix[13] = 0;
                viewDirectionMatrix[14] = 0;
                var viewDirectionProjectionMatrix = m4.multiply(projectionMatrix, viewDirectionMatrix);
                var viewDirectionProjectionInverseMatrix = m4.inverse(viewDirectionProjectionMatrix);
                // draw the skybox
                gl.useProgram(skyboxProgramInfo.program);
                webglUtils.setBuffersAndAttributes(gl, skyboxProgramInfo, quadBufferInfo);
                webglUtils.setUniforms(skyboxProgramInfo, {
                  u_viewDirectionProjectionInverse: viewDirectionProjectionInverseMatrix,
                  u_skybox: texture,
                });
                // Tell the shader to use texture unit 0 for u_texture
                gl.uniform1i(sb_textureLocation, 1);
                webglUtils.drawBufferInfo(gl, quadBufferInfo);

                requestAnimationFrame(drawScene);
            }
        }

        // Fill the buffer with the values that define a cube.
        function setGeometry(gl)
        {
            var positions = new Float32Array
            ([  
                // Front face
                -0.5, -0.5,  0.5,
                 0.5, -0.5,  0.5,
                 0.5,  0.5,  0.5,
                -0.5,  0.5,  0.5,
                // Back face
                -0.5, -0.5, -0.5,
                -0.5,  0.5, -0.5,
                 0.5,  0.5, -0.5,
                 0.5, -0.5, -0.5,
                // Top face
                -0.5,  0.5, -0.5,
                -0.5,  0.5,  0.5,
                 0.5,  0.5,  0.5,
                 0.5,  0.5, -0.5,
                // Bottom face
                -0.5, -0.5, -0.5,
                 0.5, -0.5, -0.5,
                 0.5, -0.5,  0.5,
                -0.5, -0.5,  0.5,
                // Right face
                 0.5, -0.5, -0.5,
                 0.5,  0.5, -0.5,
                 0.5,  0.5,  0.5,
                 0.5, -0.5,  0.5,
                // Left face
                -0.5, -0.5, -0.5,
                -0.5, -0.5,  0.5,
                -0.5,  0.5,  0.5,
                -0.5,  0.5, -0.5,
            ]);
            gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
        }

        // Fill the buffer with texture coordinates the cube.
        function setTexcoords(gl)
        {
            gl.bufferData
            (
                gl.ARRAY_BUFFER,
                new Float32Array
                ([
                    // Front
                    0.0,  0.0,
                    1.0,  0.0,
                    1.0,  1.0,
                    0.0,  1.0,
                    // Back
                    0.0,  0.0,
                    1.0,  0.0,
                    1.0,  1.0,
                    0.0,  1.0,
                    // Top
                    0.0,  0.0,
                    1.0,  0.0,
                    1.0,  1.0,
                    0.0,  1.0,
                    // Bottom
                    0.0,  0.0,
                    1.0,  0.0,
                    1.0,  1.0,
                    0.0,  1.0,
                    // Right
                    0.0,  0.0,
                    1.0,  0.0,
                    1.0,  1.0,
                    0.0,  1.0,
                    // Left
                    0.0,  0.0,
                    1.0,  0.0,
                    1.0,  1.0,
                    0.0,  1.0,
                ]),
            gl.STATIC_DRAW);
        }


        // Fill the buffer with vertex indices
        function setIndices(gl)
        {
            var indices = new Uint16Array
            ([  
                0,  1,  2,      0,  2,  3,    // front
                4,  5,  6,      4,  6,  7,    // back
                8,  9,  10,     8,  10, 11,   // top
                12, 13, 14,     12, 14, 15,   // bottom
                16, 17, 18,     16, 18, 19,   // right
                20, 21, 22,     20, 22, 23,   // left
            ]);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
        }

        main();
    </script>
</html>

【问题讨论】:

    标签: webgl textures skybox


    【解决方案1】:

    为了让代码正常工作,我必须做 3 件事

    1. 在绘制立方体之前绑定indexBuffer

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
      
    2. 不要在底部设置纹理

      gl.uniform1i(sb_textureLocation, 1);
      
    3. 对天空盒使用正确的纹理

              webglUtils.setUniforms(skyboxProgramInfo, {
                u_viewDirectionProjectionInverse: viewDirectionProjectionInverseMatrix,
                u_skybox: texture,      // wrong---------------
                u_skybox: sb_texture,   // right---------------
              });
      

    一些事情。

    1. webglUtils.setBuffersAndAttributes 设置绘制给定对象所需的所有缓冲区和属性。在这种情况下,这意味着当您调用

            webglUtils.setBuffersAndAttributes(gl, skyboxProgramInfo, quadBufferInfo);
      

      天空盒所需的索引绑定到 ELEMENT_ARRAY_BUFFER。 这意味着第二次通过drawScene indexBuffer 未绑定 为你的立方体。

    2. webglUtils.setUniforms 为您管理活动纹理单元。这意味着这个电话

              webglUtils.setUniforms(skyboxProgramInfo, {
                u_viewDirectionProjectionInverse: viewDirectionProjectionInverseMatrix,
                u_skybox: texture,
              });
      

      texture 设置为活动单元 0。setUniforms 只是从 0 开始,并针对使用的每个纹理进行计数。 texture u_skybox 的纹理错误,这就是您收到错误的原因。上面的代码转换为

       gl.uniformMatrix4fv(u_viewDirectionProjectionInverseLocation, false, viewDirectionProjectionInverseMatrix);
       gl.activeTexture(gl.TEXTURE0 + 0);
       gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
       gl.uniform1i(u_skyboxLocation, 0);
      

    纹理单位通常只在绘制时才关心,而不是在初始化时。它们是一组全局位置,用于为下一次绘制调用附加纹理。在每次抽奖调用之间,您都应该设置它们,但是对于您将要进行的抽奖来说,这是必需的。

    For each texture the shaders used by the next draw call need
        gl.activeTexture(gl.TEXTURE0 + n);
        gl.bindTexture(targetTypeForTexture, texture);
        gl.uniform1i(n);
    

    另见https://webglfundamentals.org/webgl/lessons/webgl-texture-units.html

    "use strict";
    
    function main() {
      // Get A WebGL context
      /** @type {HTMLCanvasElement} */
      var canvas = document.getElementById("canvas");
      var gl = canvas.getContext("webgl");
      if (!gl) {
        return;
      }
      // setup GLSL program
      var program = webglUtils.createProgramFromScripts(gl, ["3d-vertex-shader", "3d-fragment-shader"]);
      // look up where the vertex data needs to go.
      var positionLocation = gl.getAttribLocation(program, "a_position");
      var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");
      // lookup uniforms
      var matrixLocation = gl.getUniformLocation(program, "u_matrix");
      var textureLocation = gl.getUniformLocation(program, "u_texture");
    
      //create program for skybox
      const skyboxProgramInfo = webglUtils.createProgramInfo(gl, ["skybox-vertex-shader", "skybox-fragment-shader"]);
      var sb_textureLocation = gl.getUniformLocation(skyboxProgramInfo.program, "u_skybox");
      // create buffers and fill with vertex data
      const cubeBufferInfo = primitives.createCubeBufferInfo(gl, 1);
      const quadBufferInfo = primitives.createXYQuadBufferInfo(gl);
      // Create a texture.
      const sb_texture = gl.createTexture();
      gl.activeTexture(gl.TEXTURE0 + 1);
      gl.bindTexture(gl.TEXTURE_CUBE_MAP, sb_texture);
      const faceInfos = [
        {
          target: gl.TEXTURE_CUBE_MAP_POSITIVE_X,
          url: 'https://webglfundamentals.org/webgl/resources/images/computer-history-museum/pos-x.jpg',
        },
        {
          target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
          url: 'https://webglfundamentals.org/webgl/resources/images/computer-history-museum/neg-x.jpg',
        },
        {
          target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
          url: 'https://webglfundamentals.org/webgl/resources/images/computer-history-museum/pos-y.jpg',
        },
        {
          target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
          url: 'https://webglfundamentals.org/webgl/resources/images/computer-history-museum/neg-y.jpg',
        },
        {
          target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
          url: 'https://webglfundamentals.org/webgl/resources/images/computer-history-museum/pos-z.jpg',
        },
        {
          target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
          url: 'https://webglfundamentals.org/webgl/resources/images/computer-history-museum/neg-z.jpg',
        },
      ];
      faceInfos.forEach((faceInfo) => {
        const {
          target,
          url
        } = faceInfo;
        // Upload the canvas to the cubemap face.
        const level = 0;
        const internalFormat = gl.RGBA;
        const width = 512;
        const height = 512;
        const format = gl.RGBA;
        const type = gl.UNSIGNED_BYTE;
        // setup each face so it's immediately renderable
        gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null);
        // Asynchronously load an image
        const image = new Image();
        image.src = url;
        image.crossOrigin = 'anonymous';
        image.addEventListener('load', function() {
          // Now that the image has loaded make copy it to the skybox texture.
          gl.activeTexture(gl.TEXTURE0 + 1);
          gl.bindTexture(gl.TEXTURE_CUBE_MAP, sb_texture);
          gl.texImage2D(target, level, internalFormat, format, type, image);
          gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
        });
      });
      gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
      gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
    
    
      // Create a buffer for positions
      var positionBuffer = gl.createBuffer();
      // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
      gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
      // Put the positions in the buffer
      setGeometry(gl);
    
      // Create a buffer for positions
      var indexBuffer = gl.createBuffer();
      // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
      // Put the positions in the buffer
      setIndices(gl);
    
      // provide texture coordinates for the rectangle.
      var texcoordBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
      // Set Texcoords.
      setTexcoords(gl);
      // Create a texture.
      var texture = gl.createTexture();
      //void gl.bindTexture(target, texture);
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texture);
      // fill texture with 3x2 pixels
      const level = 0;
      const internalFormat = gl.RGB;
      const width = 2;
      const height = 2;
      const border = 0;
      const format = gl.RGB;
      const type = gl.UNSIGNED_BYTE;
      const data = new Uint8Array([
        255, 0, 0, 0, 255, 0,
        0, 0, 255, 128, 128, 128,
      ]);
      const alignment = 1;
      gl.pixelStorei(gl.UNPACK_ALIGNMENT, alignment);
      gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, format, type, data);
      // set the filtering so we don't need mips and it's not filtered
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    
      function degToRad(d) {
        return d * Math.PI / 180;
      }
      var fieldOfViewRadians = degToRad(60);
      var modelXRotationRadians = degToRad(0);
      var modelYRotationRadians = degToRad(0);
      // Get the starting time.
      var then = 0;
      requestAnimationFrame(drawScene);
      // Draw the scene.
      function drawScene(time) {
        // convert to seconds
        time *= 0.001;
        // Subtract the previous time from the current time
        var deltaTime = time - then;
        // Remember the current time for the next frame.
        then = time;
        webglUtils.resizeCanvasToDisplaySize(gl.canvas);
        // Tell WebGL how to convert from clip space to pixels
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.enable(gl.CULL_FACE);
        gl.enable(gl.DEPTH_TEST);
        // Animate the rotation
        modelYRotationRadians += -0.7 * deltaTime;
        modelXRotationRadians += -0.4 * deltaTime;
        // Clear the canvas AND the depth buffer.
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // Tell it to use our program (pair of shaders)
        gl.useProgram(program);
    
        // Turn on the position attribute
        gl.enableVertexAttribArray(positionLocation);
        // Bind the position buffer.
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
        var size = 3; // 3 components per iteration
        var type = gl.FLOAT; // the data is 32bit floats
        var normalize = false; // don't normalize the data
        var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
        var offset = 0; // start at the beginning of the buffer
        gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);
    
        // Turn on the teccord attribute
        gl.enableVertexAttribArray(texcoordLocation);
        // Bind the position buffer.
        gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
        // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
        var size = 2; // 2 components per iteration
        var type = gl.FLOAT; // the data is 32bit floats
        var normalize = false; // don't normalize the data
        var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
        var offset = 0; // start at the beginning of the buffer
        gl.vertexAttribPointer(texcoordLocation, size, type, normalize, stride, offset);
        
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    
        // Compute the projection matrix
        var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        var projectionMatrix = m4.perspective(fieldOfViewRadians, aspect, 1, 2000);
        var cameraPosition = [0, 0, 2];
        var up = [0, 1, 0];
        var target = [0, 0, 0];
        // Compute the camera's matrix using look at.
        var cameraMatrix = m4.lookAt(cameraPosition, target, up);
        // Make a view matrix from the camera matrix.
        var viewMatrix = m4.inverse(cameraMatrix);
        var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);
        var matrix = m4.xRotate(viewProjectionMatrix, modelXRotationRadians);
        matrix = m4.yRotate(matrix, modelYRotationRadians);
        // Set the matrix.
        gl.uniformMatrix4fv(matrixLocation, false, matrix);
        // Tell the shader to use texture unit 0 for u_texture
        gl.uniform1i(textureLocation, 0);
        // Draw the geometry.
        gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
    
        // Skybox: we only care about direction so remove the translation
        var viewDirectionMatrix = m4.copy(viewMatrix);
        viewDirectionMatrix[12] = 0;
        viewDirectionMatrix[13] = 0;
        viewDirectionMatrix[14] = 0;
        var viewDirectionProjectionMatrix = m4.multiply(projectionMatrix, viewDirectionMatrix);
        var viewDirectionProjectionInverseMatrix = m4.inverse(viewDirectionProjectionMatrix);
        // draw the skybox
        gl.useProgram(skyboxProgramInfo.program);
        webglUtils.setBuffersAndAttributes(gl, skyboxProgramInfo, quadBufferInfo);
        webglUtils.setUniforms(skyboxProgramInfo, {
          u_viewDirectionProjectionInverse: viewDirectionProjectionInverseMatrix,
          u_skybox: sb_texture,
        });
        // Tell the shader to use texture unit 0 for u_texture
        webglUtils.drawBufferInfo(gl, quadBufferInfo);
    
        requestAnimationFrame(drawScene);
      }
    }
    
    // Fill the buffer with the values that define a cube.
    function setGeometry(gl) {
      var positions = new Float32Array([
        // Front face
        -0.5, -0.5, 0.5,
        0.5, -0.5, 0.5,
        0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
        // Back face
        -0.5, -0.5, -0.5, -0.5, 0.5, -0.5,
        0.5, 0.5, -0.5,
        0.5, -0.5, -0.5,
        // Top face
        -0.5, 0.5, -0.5, -0.5, 0.5, 0.5,
        0.5, 0.5, 0.5,
        0.5, 0.5, -0.5,
        // Bottom face
        -0.5, -0.5, -0.5,
        0.5, -0.5, -0.5,
        0.5, -0.5, 0.5, -0.5, -0.5, 0.5,
        // Right face
        0.5, -0.5, -0.5,
        0.5, 0.5, -0.5,
        0.5, 0.5, 0.5,
        0.5, -0.5, 0.5,
        // Left face
        -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
      ]);
      gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
    }
    
    // Fill the buffer with texture coordinates the cube.
    function setTexcoords(gl) {
      gl.bufferData(
        gl.ARRAY_BUFFER,
        new Float32Array([
          // Front
          0.0, 0.0,
          1.0, 0.0,
          1.0, 1.0,
          0.0, 1.0,
          // Back
          0.0, 0.0,
          1.0, 0.0,
          1.0, 1.0,
          0.0, 1.0,
          // Top
          0.0, 0.0,
          1.0, 0.0,
          1.0, 1.0,
          0.0, 1.0,
          // Bottom
          0.0, 0.0,
          1.0, 0.0,
          1.0, 1.0,
          0.0, 1.0,
          // Right
          0.0, 0.0,
          1.0, 0.0,
          1.0, 1.0,
          0.0, 1.0,
          // Left
          0.0, 0.0,
          1.0, 0.0,
          1.0, 1.0,
          0.0, 1.0,
        ]),
        gl.STATIC_DRAW);
    }
    
    
    // Fill the buffer with vertex indices
    function setIndices(gl) {
      var indices = new Uint16Array([
        0, 1, 2, 0, 2, 3, // front
        4, 5, 6, 4, 6, 7, // back
        8, 9, 10, 8, 10, 11, // top
        12, 13, 14, 12, 14, 15, // bottom
        16, 17, 18, 16, 18, 19, // right
        20, 21, 22, 20, 22, 23, // left
      ]);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
    }
    
    main();
    <div class = "description">
        A 3 x2 texture <br />
    </div>
    <canvas id = "canvas"></canvas>
    
        <!-- vertex shader -->
        <script id = "3d-vertex-shader" type = "x-shader/x-vertex">
            attribute vec4 a_position;
            attribute vec2 a_texcoord;
            uniform mat4 u_matrix;
            varying vec2 v_texcoord;
            void main()
            {
                // Multiply the position by the matrix.
                gl_Position = u_matrix * a_position;
                // Pass the texcoord to the fragment shader.
                v_texcoord = a_texcoord;
            }
        </script>
        <!-- fragment shader -->
        <script id = "3d-fragment-shader" type = "x-shader/x-fragment">
            precision mediump float;
            // Passed in from the vertex shader.
            varying vec2 v_texcoord;
            // The texture.
            uniform sampler2D u_texture;
            void main()
            {
                gl_FragColor = texture2D(u_texture, v_texcoord);
            }
        </script>
        <!--skybox vertex shader-->
        <script id="skybox-vertex-shader" type="x-shader/x-vertex">
            attribute vec4 a_position;
            varying vec4 v_position;
            void main() 
            {
              v_position = a_position;
              gl_Position = a_position;
            }
        </script>
        <!--skybox fragment shader-->
        <script id="skybox-fragment-shader" type="x-shader/x-fragment">
            precision mediump float;
            uniform samplerCube u_skybox;
            uniform mat4 u_viewDirectionProjectionInverse;
            varying vec4 v_position;
            void main() 
            {
              vec4 t = u_viewDirectionProjectionInverse * v_position;
              gl_FragColor = textureCube(u_skybox, normalize(t.xyz / t.w));
            }
        </script>
    <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
    <script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
    <script src="https://webglfundamentals.org/webgl/resources/primitives.js"></script>

    【讨论】:

    • 哇,非常感谢!我发现 WebGLutils 让我的事情变得复杂。我非常努力只使用 webgl 调用使其全部工作,但所有教程似乎都将事情打包成无穷无尽的函数调用函数调用函数。不知何故,纯粹的 Webgl 实际上看起来更简单,至少对于曾经用 C 语言做过 OpenGL 的人来说是这样
    • 您可能是从中间开始而不是从一开始?在某些时候,所有的 WebGL 代码都会变得太多。前 15 或 20 篇文章只使用原始 WebG,但在某些时候你应该知道这些材料。你应该知道你只需要做 5 或 6 件事。编译着色器、查找位置、设置属性和缓冲区、设置制服、绑定纹理。在这一点上,用 150 行 WebGL 将示例弄得杂乱无章。关键是如何使用基础知识来实现​​您的结果,而不是单个 WebGL 函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-18
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    相关资源
    最近更新 更多