Author: kagula
Date: 2018-03-30
Description:
这是我《WebGL Lesson 16 – rendering to textures》的学习笔记。
源文地址:http://learningwebgl.com/blog/?p=1786
Content:
WebGL中离屏渲染有三个步骤组成:
[第一步]新建frame buffer和同frame buffer关联的texture。
[第二步]render到frame buffer, 这样同frame buffer关联的texture被绘制。
[最后一步]像使用普通texture一样,使用刚才绘制好的texture.
第一步:先要建frame buffer,并同texture对象和render buffer做关联,如下
var rttFramebuffer;
var rttTexture;
function initTextureFramebuffer() {
//create frame buffer
rttFramebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
rttFramebuffer.width = 512;
rttFramebuffer.height = 512;
//create texture
rttTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, rttTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
gl.generateMipmap(gl.TEXTURE_2D);
//把texture的图片数据指针注销(交给frame buffer管理)
gl.texImage2D(gl.TEXTURE_2D, //指定目标纹理,这个值必须是gl.TEXTURE_2D
0, // 执行细节级别。0是最基本的图像级别,n表示第N级贴图细化级别
gl.RGBA, //internalFormat, 指定纹理中的颜色组件。可选的值有GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE, GL_LUMINANCE_ALPHA 等几种。
rttFramebuffer.width, rttFramebuffer.height, //纹理图像的宽、高度,必须是2的n次方。纹理图片至少要支持64个材质元素的宽、高度
0, //边框的宽度。必须为0。
gl.RGBA, //源数据的颜色格式, 不需要和internalFormat取值必须相同。
gl.UNSIGNED_BYTE, //源数据分量的数据类型。
null);//内存中指向图像数据的指针
//create render buffer
var renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
//设置当前工作的渲染缓冲的存储大小
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, //这两个参数是固定的
rttFramebuffer.width, rttFramebuffer.height);
//texture绑定到frame buffer中
//https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/framebufferTexture2D
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, //target
rttTexture, //source, A WebGLTexture object whose image to attach.
0);//A GLint specifying the mipmap level of the texture image to be attached. Must be 0.
//把render buffer绑定到frame buffer上
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
gl.RENDERBUFFER, //renderBufferTarget, A GLenum specifying the binding point (target) for the render buffer.
renderbuffer);//A WebGLRenderbuffer object to attach.
//unbind
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
上面的代码新建了rttFrameBuffer对象。
第二步:render到纹理,并让这个纹理生成MipMap
我们drawElement的时候,可以render到这个对象中。
gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
drawSceneOnLaptopScreen();
注意上面的第一行代码,我们让当前render, render到rttFramebuffer中。
drawSceneOnLaptopScreen函数中的代码片段如下(就跟原来的普通render一样写代码,只不过这次是render到rttFrameBuffer中)
gl.viewport(0, 0, rttFramebuffer.width, rttFramebuffer.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, laptopScreenAspectRatio, 0.1, 100.0, pMatrix);
。。。
gl.drawElements(gl.TRIANGLES, moonVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
。。。
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
由于rttFrameBuffer同rttTexture关联,上面的代码把scene render到了rttTexture上去。
再用下面的代码为rttTexture生成MipMap
gl.bindTexture(gl.TEXTURE_2D, rttTexture);
gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(gl.TEXTURE_2D, null);
最后一步:
用下面的代码解除同frame buffer的绑定,这样我们才能render到用户可见的屏幕上。
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
最后可以像使用普通纹理对象一样使用rttTexture。
如下
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, rttTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLE_STRIP, 0, laptopScreenVertexPositionBuffer.numItems);
Lesson16包含了太多不必要的代码,现在通过整理,可以看到实现离屏渲染不是件很难的事。