fbo在webgl中很常见,但在cesium的开发工作中基本用不到,而源码修改时,这又是必经的一条道,因此,转头回去学习了一下fbo的使用;由于原文的fbo使用了贴图,相对复杂,不利于理解,本人在理解的时候就移除了部分内容;
fbo一词尝与离屏渲染相关联,
webgl系统默认绑定的是窗口缓冲区,即绑定的shader program 会把对应的数据直接绘制到屏幕上,但是很多时候,绘制的直接结果不是我们想要的,因此可以使用fbo 即帧缓冲区。
渲染流程图,实际上渲染到fbo与渲染到屏幕上的流程没有任何差异,只有中间绑定帧缓冲区与解绑的操作。 渲染流程如下。
首先是shader准备两份,一份为取为屏幕缓冲区渲染创建的,需要片元由传入的纹理决定;另一份为离屏渲染使用的,简单的写为了 全是红色
离屏渲染的shder 屏幕渲染的shader
后续的逻辑基本就如流程图中的一致了,参照理解即可
简化fbo的渲染逻辑后的代码为
// HelloQuad.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';
// Size of off screen
var OFFSCREEN_WIDTH = 256;
var OFFSCREEN_HEIGHT = 256;
//顶点坐标,纹理坐标,模型视图矩阵,将顶点着色器中的纹理坐标传递给片元v_TexCoord纹理坐标
var m_VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
'}\n';
// 采样器,纹理坐标,纹理着色片元
var m_FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'uniform sampler2D u_Sampler;\n' +
'void main() {\n' +
' gl_FragColor = texture2D(u_Sampler, vec2(0.5,0.5));\n' +
'}\n'
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
// var ext = gl.getExtension('WEBGL_draw_buffers')
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
let initProgram = initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
var fbo = initFramebufferObject(gl);
// Write the positions of vertices to a vertex shader
var n = initVertexBuffers(gl);
//绑定帧缓冲区
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); // Change the drawing destination to FBO
gl.viewport(0, 0, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); // Set a viewport for FBO
//设置背景色
gl.clearColor(0.2, 0.2, 0.4, 1.0); // Set clear color (the color is slightly changed)
//清空颜色和深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear FBO
//一直前清空
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// Specify the color for clearing <canvas>
gl.clearColor(0, 0, 0, 1);
// Clear <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw the rectangle
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
gl.bindFramebuffer(gl.FRAMEBUFFER, null); // Change the drawing destination to color buffer
//重新设置屏幕渲染的视点
gl.viewport(0, 0, canvas.width, canvas.height); // Set the size of viewport back to that of <canvas>
let newProgram = initShaders(gl, m_VSHADER_SOURCE, m_FSHADER_SOURCE);
// Bind the texture object to the target
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fbo.texture);
initVertexBuffers(gl);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
}
function initFramebufferObject(gl) {
var framebuffer, texture, depthBuffer;
// 创建FBO 帧缓冲区
framebuffer = gl.createFramebuffer();
// Create a texture object and set its size and parameters
texture = gl.createTexture(); // Create a texture object
gl.bindTexture(gl.TEXTURE_2D, texture); // Bind the object to target
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
framebuffer.texture = texture; // Store the texture object
// 将帧缓冲区绑定到程序上
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
// The WebGLRenderingContext.framebufferTexture2D() method of the WebGL API attaches a texture to a WebGLFramebuffer.
//将framebufer渲染到一个纹理附件中
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
// 创建渲染缓冲区
depthBuffer = gl.createRenderbuffer(); // Create a renderbuffer object
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); // Bind the object to target
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);
//将帧缓冲区绑定到渲染缓冲区上
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
// 解除帧缓冲区绑定
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
//解除纹理
gl.bindTexture(gl.TEXTURE_2D, null);
//解除 渲染缓冲区
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
return framebuffer;
}
function initVertexBuffers(gl) {
var vertices = new Float32Array([
-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5
]);
var n = 4; // The number of vertices
// Create a buffer object
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// Bind the buffer object to target
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// Write date into the buffer object
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
// Assign the buffer object to a_Position variable
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// Enable the assignment to a_Position variable
gl.enableVertexAttribArray(a_Position);
return n;
}