Skip to content

高级主题:帧缓冲区、阴影映射与WebGL 2.0

一、帧缓冲区(Frame Buffer Object, FBO)

帧缓冲区对象(FBO)是WebGL中用于离屏渲染的重要工具。它允许开发者将渲染结果存储到一个纹理或渲染缓冲中,而不是直接渲染到屏幕上。这在实现高级效果(如阴影映射、后期处理等)时非常有用。

创建帧缓冲区
  1. 创建帧缓冲对象

    javascript
    const fbo = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
  2. 创建纹理附件

    javascript
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, 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);
  3. 将纹理附加到帧缓冲

    javascript
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
  4. 检查帧缓冲状态

    javascript
    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
        console.error('Framebuffer is not complete');
    }
  5. 解绑帧缓冲

    javascript
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

二、阴影映射(Shadow Mapping)

阴影映射是一种实现阴影效果的技术。它通过从光源的视角渲染场景,生成一张深度贴图(Depth Map),然后在主渲染中使用这张深度贴图来计算阴影。

实现阴影映射的步骤
  1. 创建深度贴图

    javascript
    const SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;
    const depthMapFBO = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, depthMapFBO);
    
    const depthMap = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, depthMap);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, gl.DEPTH_COMPONENT, gl.FLOAT, null);
    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.REPEAT);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
    
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthMap, 0);
    gl.drawBuffer(gl.NONE);
    gl.readBuffer(gl.NONE);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  2. 从光源视角渲染场景

    javascript
    gl.bindFramebuffer(gl.FRAMEBUFFER, depthMapFBO);
    gl.clear(gl.DEPTH_BUFFER_BIT);
    // 渲染场景
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  3. 在主渲染中使用深度贴图

    javascript
    const depthMapLocation = gl.getUniformLocation(program, 'u_depthMap');
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, depthMap);
    gl.uniform1i(depthMapLocation, 0);
  4. 片段着色器中计算阴影

    glsl
    precision mediump float;
    varying vec4 v_position;
    uniform sampler2D u_depthMap;
    uniform mat4 u_lightSpaceMatrix;
    
    void main() {
        vec4 shadowCoord = u_lightSpaceMatrix * v_position;
        shadowCoord = shadowCoord / shadowCoord.w;
        float closestDepth = texture2D(u_depthMap, shadowCoord.xy).r;
        float currentDepth = shadowCoord.z;
        float shadow = currentDepth > closestDepth ? 1.0 : 0.0;
        gl_FragColor = vec4(shadow);
    }

三、WebGL 2.0

WebGL 2.0是WebGL的更新版本,基于OpenGL ES 3.0。它引入了许多新特性,提升了WebGL的性能和功能。

WebGL 2.0的新特性
  1. 更高级的着色器语言(GLSL ES 3.00):支持更复杂的着色器功能。
  2. 多渲染目标(Multiple Render Targets, MRT):允许同时渲染到多个颜色附件。
  3. 更高效的纹理操作:支持更多的纹理格式和操作。
  4. 更强大的缓冲区对象:支持更多的缓冲区操作和更高效的数据传输。
  5. 改进的帧缓冲对象:支持更多的附件类型和更灵活的配置。
示例代码

以下是一个简单的WebGL 2.0代码示例,展示如何创建一个帧缓冲对象并渲染到纹理:

javascript
const gl = canvas.getContext('webgl2');
if (!gl) {
    console.error('WebGL 2.0 not supported');
}

const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);

const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, 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('Framebuffer is not complete');
}

gl.bindFramebuffer(gl.FRAMEBUFFER, null);

通过这些高级主题的介绍和示例代码,你可以更好地理解和应用帧缓冲区、阴影映射和WebGL 2.0的新特性。

Theme by threelab