【WebGL】fbo双pass案例

双pass渲染案例(离线渲染一个三角面,然后渲染到一个占满屏幕的矩阵上)

离线渲染如何需要开启深度测试的话,需要额外操作,这里不展开

js 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebGL 2 Offline Rendering and Texture Display</title>
</head>

<body>
    <canvas id="glCanvas" width="800" height="600"></canvas>
    <script>
        // 将 WebGL 初始化和渲染逻辑封装到一个函数中
        function initWebGL() {
            // 获取 canvas 元素
            const canvas = document.getElementById('glCanvas');
            // 获取 WebGL 2 上下文
            const gl = canvas.getContext('webgl2');

            // 如果无法获取 WebGL 2 上下文,给出提示并结束函数
            if (!gl) {
                alert('无法初始化 WebGL 2,你的浏览器可能不支持。');
                return;
            }

            // 离线渲染的着色器代码
            const offlineVertexShaderSource = `#version 300 es
                in vec2 a_position;
                void main() {
                    gl_Position = vec4(a_position, 0.0, 1.0);
                }
            `;

            const offlineFragmentShaderSource = `#version 300 es
                precision mediump float;
                out vec4 fragColor;
                void main() {
                    fragColor = vec4(1.0, 0.0, 0.0, 1.0);
                }
            `;

            // 最终渲染到屏幕的着色器代码
            const finalVertexShaderSource = `#version 300 es
                in vec2 a_position;
                in vec2 a_texCoord;
                out vec2 v_texCoord;
                void main() {
                    gl_Position = vec4(a_position, 0.0, 1.0);
                    v_texCoord = a_texCoord;
                }
            `;

            const finalFragmentShaderSource = `#version 300 es
                precision mediump float;
                in vec2 v_texCoord;
                uniform sampler2D u_texture;
                out vec4 fragColor;
                void main() {
                    fragColor = texture(u_texture, v_texCoord);
                }
            `;

            // 创建着色器函数
            function createShader(gl, type, source) {
                const shader = gl.createShader(type);
                gl.shaderSource(shader, source);
                gl.compileShader(shader);
                const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
                if (success) {
                    return shader;
                }
                console.log(gl.getShaderInfoLog(shader));
                gl.deleteShader(shader);
            }

            // 创建着色器程序函数
            function createProgram(gl, vertexShader, fragmentShader) {
                const program = gl.createProgram();
                gl.attachShader(program, vertexShader);
                gl.attachShader(program, fragmentShader);
                gl.linkProgram(program);
                const success = gl.getProgramParameter(program, gl.LINK_STATUS);
                if (success) {
                    return program;
                }
                console.log(gl.getProgramInfoLog(program));
                gl.deleteProgram(program);
            }

            // 创建离线渲染的着色器程序
            const offlineVertexShader = createShader(gl, gl.VERTEX_SHADER, offlineVertexShaderSource);
            const offlineFragmentShader = createShader(gl, gl.FRAGMENT_SHADER, offlineFragmentShaderSource);
            const offlineProgram = createProgram(gl, offlineVertexShader, offlineFragmentShader);

            // 创建最终渲染的着色器程序
            const finalVertexShader = createShader(gl, gl.VERTEX_SHADER, finalVertexShaderSource);
            const finalFragmentShader = createShader(gl, gl.FRAGMENT_SHADER, finalFragmentShaderSource);
            const finalProgram = createProgram(gl, finalVertexShader, finalFragmentShader);

            // 离线渲染的顶点数据
            const offlinePositions = [
                -0.5, -0.5,
                0.5, -0.5,
                0.0, 0.5
            ];

            // 创建离线渲染的顶点缓冲区
            const offlinePositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, offlinePositionBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(offlinePositions), gl.STATIC_DRAW);

            // 获取离线渲染的顶点属性位置
            const offlinePositionAttributeLocation = gl.getAttribLocation(offlineProgram, 'a_position');
            gl.enableVertexAttribArray(offlinePositionAttributeLocation);
            gl.vertexAttribPointer(offlinePositionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

            // 创建帧缓冲区对象(FBO)
            const framebuffer = gl.createFramebuffer();
            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

            // 创建纹理对象
            const texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            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);

            // 将纹理附加到帧缓冲区
            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

            // 检查帧缓冲区是否完整
            if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
                console.error('帧缓冲区不完整');
            }

            // 离线渲染
            gl.useProgram(offlineProgram);
            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
            gl.viewport(0, 0, canvas.width, canvas.height);
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.drawArrays(gl.TRIANGLES, 0, 3);

            // 最终渲染到屏幕的顶点数据和纹理坐标
            const finalPositions = [
                -1.0, -1.0,
                1.0, -1.0,
                -1.0, 1.0,
                -1.0, 1.0,
                1.0, -1.0,
                1.0, 1.0
            ];

            const finalTexCoords = [
                0.0, 0.0,
                1.0, 0.0,
                0.0, 1.0,
                0.0, 1.0,
                1.0, 0.0,
                1.0, 1.0
            ];

            // 创建最终渲染的顶点缓冲区和纹理坐标缓冲区
            const finalPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, finalPositionBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(finalPositions), gl.STATIC_DRAW);

            const finalTexCoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, finalTexCoordBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(finalTexCoords), gl.STATIC_DRAW);

            // 获取最终渲染的顶点属性位置和纹理坐标属性位置
            const finalPositionAttributeLocation = gl.getAttribLocation(finalProgram, 'a_position');
            gl.enableVertexAttribArray(finalPositionAttributeLocation);
            gl.vertexAttribPointer(finalPositionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

            const finalTexCoordAttributeLocation = gl.getAttribLocation(finalProgram, 'a_texCoord');
            gl.enableVertexAttribArray(finalTexCoordAttributeLocation);
            gl.vertexAttribPointer(finalTexCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);

            // 获取最终渲染的纹理采样器位置
            const textureUniformLocation = gl.getUniformLocation(finalProgram, 'u_texture');

            // 最终渲染到屏幕
            gl.useProgram(finalProgram);
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            gl.viewport(0, 0, canvas.width, canvas.height);
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.uniform1i(textureUniformLocation, 0);
            gl.drawArrays(gl.TRIANGLES, 0, 6);
        }

        // 调用封装好的函数来初始化和渲染 WebGL
        initWebGL();
    </script>
</body>

</html>
相关推荐
GISer_Jing4 小时前
height、line-height、font-size相同字体遮挡问题
前端·ai
字节渡客4 小时前
接口参数偶尔丢失、乱码、解析失败,不是前端传参问题
前端
夏幻灵4 小时前
前端八股:JavaScript 深拷贝详解:实现方式、递归原理与循环引用
开发语言·前端·javascript
@#¥&~是乱码鱼啦5 小时前
ArkWeb开发手记01|Web 组件 H5 加载与生命周期管理
前端
suaizai_5 小时前
ComputerUse:让AI真正操控桌面系统
java·前端·javascript
CodeSheep5 小时前
朋友面试谈薪报价2w,HR非得压到1.9,还反问他:你就差这1000块钱?后来背调时问了他前同事十几个问题,对方最后直接挂了
前端·后端·程序员
IMPYLH5 小时前
HTML 的 <track> 元素
前端·html
涛涛ing5 小时前
35个组件,把GPU特效直接跑在真实DOM上:Canvas UI正在重新定义Web视觉
前端
乘风gg5 小时前
Spec Kit vs OpenSpec vs Superpowers:8 个 Skill 的落地实录与工程方法论
前端·ai编程·claude
FYKJ_20105 小时前
express绿叶横店短剧推荐与影评分享平台34219-计算机课程设计、毕业设计
java·javascript·vue.js·spring boot·后端·课程设计·express