【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>
相关推荐
lh_125411 分钟前
ECharts 地图开发入门
前端·javascript·echarts
jjw_zyfx13 分钟前
成熟的前端vue vite websocket,Django后端实现方案包含主动断开websocket连接的实现
前端·vue.js·websocket
Mikey_n1 小时前
前台调用接口的方式及速率对比
前端
周之鸥1 小时前
使用 Electron 打包可执行文件和资源:完整实战教程
前端·javascript·electron
我爱吃朱肉1 小时前
HTMLCSS模板实现水滴动画效果
前端·css·css3
机器视觉知识推荐、就业指导1 小时前
开源QML控件:进度条滑动控件(含源码下载链接)
前端·qt·开源·qml
前端snow1 小时前
前端全栈第二课:用typeorm向数据库添加数据---一对多关系
前端·javascript
難釋懷1 小时前
Shell脚本-for循环语法结构
前端·chrome
全栈老李技术面试1 小时前
【高频考点精讲】async/await原理剖析:Generator和Promise的完美结合
前端·javascript·css·vue·html·react·面试题
kadog1 小时前
PubMed PDF下载 cloudpmc-viewer-pow逆向
前端·javascript·人工智能·爬虫·pdf