2022-10-17-----------------天空盒(二)

128 阅读3分钟

一、原理

目前我们是首先渲染天空盒,之后再渲染场景中的其它物体。这样子能够工作,但不是非常高效。如果我们先渲染天空盒,我们就会对屏幕上的每一个像素运行一遍片段着色器,即便只有一小部分的天空盒最终是可见的。可以使用提前深度测试(Early Depth Testing)轻松丢弃掉的片段能够节省我们很多宝贵的带宽。

所以,我们将会最后渲染天空盒,以获得轻微的性能提升。这样子的话,深度缓冲就会填充满所有物体的深度值了,我们只需要在提前深度测试通过的地方渲染天空盒的片段就可以了,很大程度上减少了片段着色器的调用。问题是,天空盒只是一个1x1x1的立方体,它很可能会不通过大部分的深度测试,导致渲染失败。不用深度测试来进行渲染不是解决方案,因为天空盒将会复写场景中的其它物体。我们需要欺骗深度缓冲,让它认为天空盒有着最大的深度值1.0,只要它前面有一个物体,深度测试就会失败。

坐标系统小节中我们说过:
透视除法\color{red}{透视除法}是在顶点着色器运行之后执行的,将gl_Positionxyz坐标除以w分量。我们又从深度测试小节中知道,相除结果的z分量等于顶点的深度值。使用这些信息,我们可以将输出位置的z分量等于它的w分量,让z分量永远等于1.0,这样子的话,当透视除法执行之后,z分量会变为z/w=1.0\color{orange}{z / w = 1.0}。 首先开启提前深度测试:

 gl.depthFunc(gl.LEQUAL);

顶点着色器修改

mat4 relativeViewMatrix=u_ViewMatrix;
relativeViewMatrix[3].x=0.0; 
relativeViewMatrix[3].y=0.0; 
relativeViewMatrix[3].z=0.0; 
vec4 clipPosition=u_ProjectMatrix*relativeViewMatrix*u_ModelMatrix * a_Position;
gl_Position =clipPosition.xyww;

二、代码

 //顶点着色器
    var skyBox_VS = /*glsl*/ `
      attribute vec4 a_Position;
      uniform mat4 u_ModelMatrix; 
      uniform mat4 u_ViewMatrix; 
      uniform mat4 u_ProjectMatrix; 
    
      varying vec3 v_Position;
      void main(){
        mat4 relativeViewMatrix=u_ViewMatrix;
        relativeViewMatrix[3].x=0.0; 
        relativeViewMatrix[3].y=0.0; 
        relativeViewMatrix[3].z=0.0; 
        vec4 clipPosition=u_ProjectMatrix*relativeViewMatrix*u_ModelMatrix * a_Position;
      
    
         gl_Position =clipPosition.xyww;
         v_Position=a_Position.rgb;

      }`;

    //片元着色器
    var skyBox_FS = /*glsl*/ `
      #ifdef GL_ES
      precision mediump float;
      #endif
     
      uniform samplerCube u_CubeMapTexture;
       uniform vec3 u_AmbitionColor;
      varying vec3 v_Position;


      void main(){
        gl_FragColor=textureCube(u_CubeMapTexture, v_Position);

      }`;

    var v_shader =/*glsl*/`
       attribute vec4 a_Position;
        attribute vec2 a_Uv;

       uniform mat4 u_ModelMatrix; 
      uniform mat4 u_ViewMatrix; 
      uniform mat4 u_ProjectMatrix; 
      varying vec2 v_Uv;
      void main(){
        gl_Position=u_ProjectMatrix*u_ViewMatrix*u_ModelMatrix*a_Position;
        v_Uv=a_Uv;
      }
    `

    var f_shader =/*glsl*/`
     #ifdef GL_ES
      precision mediump float;
      #endif
        varying vec2 v_Uv;
        uniform sampler2D u_Texture;
        void main(){
            vec4 baseColor=texture2D(u_Texture,v_Uv);
            gl_FragColor=baseColor;
        }

    `


    //声明js需要的相关变量
    var canvas = document.getElementById("canvas");
    var gl = getWebGLContext(canvas);
    var boxProgram;
    var skyboxProgram
    async function main() {
        if (!gl) {
            console.log("你的浏览器不支持WebGL");
            return;
        }

        skyboxProgram = createProgram(gl, skyBox_VS, skyBox_FS);
        if (!skyboxProgram) {
            console.warn("创建程序失败!");
            return;
        }

        boxProgram = createProgram(gl, v_shader, f_shader)
        if (!boxProgram) {
            console.warn("创建立方体程序失败!");
            return;
        }



        //设置透视投影矩阵
        var projMatrix = new Matrix4();
        projMatrix.setPerspective(30, canvas.width / canvas.height, 0.1, 100);

        //设置视角矩阵的相关信息(视点,视线,上方向)
        var viewMatrix = new Matrix4();
        viewMatrix.setLookAt(0, 0, 4, 0, 0, 0, 0, 1, 0);




        //开启隐藏面清除
        gl.enable(gl.DEPTH_TEST);
        // gl.enable(gl.CULL_FACE)



        gl.program = skyboxProgram;
        gl.useProgram(skyboxProgram);
        //获取内置变量的信息
        getVariableLocation();

        var n = createCube(gl);
        if (n < 0) {
            console.log("无法创建缓冲区");
            return;
        }


        await initCubeTexture(
            [
                "./image/skybox/right.jpg",
                "./image/skybox/left.jpg",
                "./image/skybox/top.jpg",
                "./image/skybox/bottom.jpg",

                "./image/skybox/front.jpg",
                "./image/skybox/back.jpg",
            ]
        )



        gl.useProgram(boxProgram)
        gl.program = boxProgram

        //获取内置变量的信息
        getVariableLocation();
        var n = createCube(gl);
        if (n < 0) {
            console.log("无法创建缓冲区");
            return;
        }
        //初始化纹理
        await initTexture(gl, "./image/box.jpg", 0);


        document.addEventListener("keydown", (e) => {

            if (e.key === "ArrowUp") {
                viewMatrix.translate(0, 0, 1)
            } else if (e.key === "ArrowDown") {
                viewMatrix.translate(0, 0, -1)
            } else if (e.key === "ArrowLeft") {
                viewMatrix.rotate(-1, 0, 1, 0)
            } else if (e.key === "ArrowRight") {
                viewMatrix.rotate(1, 0, 1, 0)
            }


        });




        // await draw(projMatrix,viewMatrix)
        //设置底色


        gl.depthFunc(gl.LEQUAL);
        //根据时间绘制
        var tick = async function () {

            await draw(projMatrix, viewMatrix)

            //重复请求
            requestAnimationFrame(tick)
        }
        tick()




    }

    async function draw(projMatrix, viewMatrix) {
        //清空颜色和深度缓冲区
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // gl.depthMask(true);
        gl.useProgram(boxProgram)
        gl.program = boxProgram


        await drawBox(projMatrix, viewMatrix)

        // gl.depthMask(false);
        gl.useProgram(skyboxProgram)
        gl.program = skyboxProgram


        //绘制三角形
        await drawSkyBox(projMatrix, viewMatrix);

    }





    async function drawSkyBox(projMatrix, viewMatrix) {
        const program = gl.program;
        //设置视角矩阵的相关信息(视点,视线,上方向)






        //将试图矩阵传给u_ViewMatrix变量
        gl.uniformMatrix4fv(program.projectMatrix, false, projMatrix.elements);


        //设置模型矩阵的相关信息
        var modelMatrix = new Matrix4();
        gl.uniformMatrix4fv(program.modelMatrix, false, modelMatrix.elements);


        gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.elements);



        //绘制图形
        gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);
    }


    async function drawBox(projMatrix, viewMatrix) {
        const program = gl.program;



        //设置模型矩阵的相关信息
        var modelMatrix = new Matrix4();
        gl.uniformMatrix4fv(program.modelMatrix, false, modelMatrix.elements);


        gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.elements);


        //设置透视投影矩阵
        var projMatrix = new Matrix4();
        projMatrix.setPerspective(30, canvas.width / canvas.height, 0.1, 100);
        //将试图矩阵传给u_ViewMatrix变量
        gl.uniformMatrix4fv(program.projectMatrix, false, projMatrix.elements);

        //绘制图形
        gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);
    }