2022-10-14-------------------天空盒(一)

137 阅读1分钟

一、原理

1.创建一个立方体贴图,代码见3.1

2.关闭深度写入,先绘制一个盒子,纹理为立方体贴图

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

        //绘制天空盒
        await drawSkyBox(projMatrix, viewMatrix);

shader:


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

      }`;

3.打开深度写入,绘制其他物体

        gl.depthMask(true);
        gl.useProgram(boxProgram)
        gl.program = boxProgram


        await drawBox(projMatrix, viewMatrix)

4.移动时不让天空盒移动,即将mvp矩阵的平移分量设为0

       mat4 relativeViewMatrix=u_ViewMatrix;
        relativeViewMatrix[3].x=0.0; 
        relativeViewMatrix[3].y=0.0; 
        relativeViewMatrix[3].z=0.0; 

二、效果

skybox.gif

##三、 代码 3.1 立方体贴图

 * 初始化立方体纹理
 * @param {*} srcArr 
 */
async function initCubeTexture(srcArr) {
    //加载纹理
    const imgArr = []
    for (let i = 0; i < 6; i++) {
        var image = new Image();
        image.src = srcArr[i];
        image = await reloadImage(image);
        imgArr.push(image)
    }


    const cubeTexture = createCubeMapTexture(imgArr)

    //绑定
    gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTexture);
    //赋值
    gl.uniform1i(gl.program.cubeMapTexture, 0)
}

/**glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
 * 创建立方体纹理
 * @param {*图片数组} imgArr 
 * @returns 
 */
function createCubeMapTexture(imgArr) {
    //创建纹理
    const texture = gl.createTexture()
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
    //激活0号纹理单元
    gl.activeTexture(gl.TEXTURE0)
    //绑定立方体纹理
    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
    //给立方体纹理每个面分配图像
    for (let i = 0; i < 6; i++) {
        gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
            0,
            gl.RGB,
            gl.RGB,
            gl.UNSIGNED_BYTE,
            imgArr[i])
    }
    //设置纹理采样方式
    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    // gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WARP_S, gl.CLAMP_TO_EDGE);
    // gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WARP_T, gl.CLAMP_TO_EDGE);
    // gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WARP_R, gl.CLAMP_TO_EDGE);


    //设置立方体纹理为空
    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);

    return texture


}

3.2.绘制天空盒和箱子


    //顶点着色器
    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; 
         gl_Position = u_ProjectMatrix*relativeViewMatrix*u_ModelMatrix * a_Position;
         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(){
            gl_FragColor=texture2D(u_Texture,v_Uv);
        }

    `


    //声明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)
         //设置底色
       
        //根据时间绘制
        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(false);
        gl.useProgram(skyboxProgram)
        gl.program=skyboxProgram
        
        //绘制三角形
        await drawSkyBox(projMatrix,viewMatrix);


        gl.depthMask(true);
        gl.useProgram(boxProgram)
        gl.program=boxProgram
    
        
        await drawBox(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);
    }