WebGL 渲染三维图形作为纹理贴到另一个三维物体表面

news2024/11/24 4:20:49

目录

渲染到纹理 

帧缓冲区对象和渲染缓冲区对象

帧缓冲区对象

帧缓冲区对象的结构

如何实现渲染到纹理 

示例程序(FramebufferObject.js)

创建帧缓冲区对象(gl.createFramebuffer())

gl.createFramebuffer()规范 

gl.deleteFramebuffer()规范 

创建纹理对象并设置其尺寸和参数

创建渲染缓冲区对象(gl.createRenderbuffer())

gl.createRenderbuffer()规范

​编辑gl.deleteRenderbuffer() 规范

绑定渲染缓冲区并设置其尺寸(gl.bindRenderbuffer(),gl.renderbufferStorage()) 

gl.bindRenderbuffer()规范

gl.renderbufferStorage()规范

将纹理对象关联到帧缓冲区颜色关联对象(gl.bindFramebuffer(),gl.framebufferTexture2D()) 

gl.bindFramebuffer()规范

将渲染缓冲区对象关联到帧缓冲区对象(gl.framebufferRenderbuffer()) 

gl.framebufferRenderbuffer()规范

检查帧缓冲区的配置(gl.checkFramebufferStatus()) 

gl.checkFramebufferStatus()规范 

在帧缓冲区进行绘图

gl.viewport()规范​


渲染到纹理 

WebGL简单而又强大的技术是,使用WebGL渲染三维图形,然后将渲染结果作为纹理贴到另一个三维物体上去。实际上,把渲染结果作为纹理使用,就是动态地生成图像,而不是向服务器请求加载外部图像。在纹理图像被贴上图形之前,我们还可以对其做一些额外的处理,比如生成如动态模糊或景深效果。本文将创建了一个新的示例程序FramebufferObject,将一个旋转的立方体作为纹理贴在一个矩形上,如下图所示。

运行程序,你会看到场景中有一个矩形,矩形的纹理中有一个正在旋转的立方体,立方体的纹理是蓝天白云。最重要的是,矩形的纹理并不是事先准备好的,而是WebGL实时绘制出来的。这项技术很有用,所以我们来研究一下究竟怎样做才能达到这样的效果。 

帧缓冲区对象和渲染缓冲区对象

在默认情况下,WebGL在颜色缓冲区中进行绘图,在开启隐藏面消除功能时,还会用到深度缓冲区。总之,绘制的结果图像是存储在颜色缓冲区中的。

帧缓冲区对象

帧缓冲区对象(framebuffer object)可以用来代替颜色缓冲区或深度缓冲区,如下图所示。绘制在帧缓冲区中的对象并不会直接显示在<canvas>上,你可以先对帧缓冲区中的内容进行一些处理再显示,或者直接用其中的内容作为纹理图像。在帧缓冲区中进行绘制的过程又称为离屏绘制(offscreen drawing)。

帧缓冲区对象的结构

下图显示了帧缓冲区对象的结构,它提供了颜色缓冲区和深度缓冲区的替代品。如你所见,绘制操作并不是直接发生在帧缓冲区中的,而是发生在帧缓冲区所关联的对象 

经过一些设置,WebGL就可以向帧缓冲区的关联对象中写入数据,就像写入颜色缓冲区或深度缓冲区一样。每个关联对象又可以是两种类型的:纹理对象或渲染缓冲区对象(renderbuffer object)。纹理对象存储了纹理图像。当我们把纹理对象作为颜色关联对象关联到帧缓冲区对象后,WebGL就可以在纹理对象中绘图。渲染缓冲区对象表示一种更加通用的绘图区域,可以向其中写入多种类型的数据。 

如何实现渲染到纹理 

 如上所述,我们希望把WebGL渲染出的图像作为纹理使用,那么就需要将纹理对象作为颜色关联对象关联到帧缓冲区对象上,然后在帧缓冲区中进行绘制,此时颜色关联对象(即纹理对象)就替代了颜色缓冲区。此时仍然需要进行隐藏面消除,所以我们又创建了一个渲染缓冲区对象来作为帧缓冲区的深度关联对象,以替代深度缓冲区。帧缓冲区的设置如下图所示。

帧缓冲区的配置情况

以下是实现上述配置的8个步骤。

1.创建帧缓冲区对象(gl.createFramebffer())。

2.创建纹理对象并设置其尺寸和参数(gl.createTexture()、gl.bindTexture()、gl.texImage2D()、gl.Parameteri())。

3.创建渲染缓冲区对象(gl.createRenderbuffer())。

4.绑定渲染缓冲区对象并设置其尺寸(gl.bindRenderbuffer()、gl.renderbuffer-Storage())。

5.将帧缓冲区的颜色关联对象指定为一个纹理对象(gl.frambufferTexture2D())。

6. 将帧缓冲区的深度关联对象指定为一个渲染缓冲区对象(gl.framebufferRenderbuffer())。

7.检查帧缓冲区是否正确配置(gl.checkFramebufferStatus())。

8.在帧缓冲区中进行绘制(gl.bindFramebuffer())。

现在来看一下示例程序。 

示例程序(FramebufferObject.js)

var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec2 a_TexCoord;\n' +
  'uniform mat4 u_MvpMatrix;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_Position = u_MvpMatrix * a_Position;\n' +
  '  v_TexCoord = a_TexCoord;\n' +
  '}\n';
var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  '#endif\n' +
  'uniform sampler2D u_Sampler;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
  '}\n';

// 离屏绘制的尺寸
var OFFSCREEN_WIDTH = 256;
var OFFSCREEN_HEIGHT = 256;
function main() {
  var canvas = document.getElementById('webgl');
  var gl = getWebGLContext(canvas);
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) return

  // 获取属性变量和统一变量的存储位置
  var program = gl.program; // 获取程序对象
  program.a_Position = gl.getAttribLocation(program, 'a_Position');
  program.a_TexCoord = gl.getAttribLocation(program, 'a_TexCoord');
  program.u_MvpMatrix = gl.getUniformLocation(program, 'u_MvpMatrix');
  var cube = initVertexBuffersForCube(gl);
  var plane = initVertexBuffersForPlane(gl);
  var texture = initTextures(gl); // 设置纹理

  // 初始化帧缓冲区对象(FBO)
  var fbo = initFramebufferObject(gl);
  gl.enable(gl.DEPTH_TEST);   // 开启深度测试
  var viewProjMatrix = new Matrix4();   // 为颜色缓冲区所准备
  viewProjMatrix.setPerspective(30, canvas.width/canvas.height, 1.0, 100.0);
  viewProjMatrix.lookAt(0.0, 0.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

  var viewProjMatrixFBO = new Matrix4();   // 为帧缓冲区所准备
  viewProjMatrixFBO.setPerspective(30.0, OFFSCREEN_WIDTH/OFFSCREEN_HEIGHT, 1.0, 100.0);
  viewProjMatrixFBO.lookAt(0.0, 2.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

  var currentAngle = 0.0; // 当前旋转角度(度)
  var tick = function() {
    currentAngle = animate(currentAngle);  // 更新当前旋转角度
    draw(gl, canvas, fbo, plane, cube, currentAngle, texture, viewProjMatrix, viewProjMatrixFBO);
    window.requestAnimationFrame(tick, canvas);
  };
  tick();
}

function initVertexBuffersForCube(gl) { // 立方体数据
  // 创建一个立方体
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3
  var vertices = new Float32Array([
     1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0,    // v0-v1-v2-v3 front
     1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0,    // v0-v3-v4-v5 right
     1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0,    // v0-v5-v6-v1 up
    -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0,    // v1-v6-v7-v2 left
    -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0,    // v7-v4-v3-v2 down
     1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0     // v4-v7-v6-v5 back
  ]);
  var texCoords = new Float32Array([
      1.0, 1.0,   0.0, 1.0,   0.0, 0.0,   1.0, 0.0,    // v0-v1-v2-v3 front
      0.0, 1.0,   0.0, 0.0,   1.0, 0.0,   1.0, 1.0,    // v0-v3-v4-v5 right
      1.0, 0.0,   1.0, 1.0,   0.0, 1.0,   0.0, 0.0,    // v0-v5-v6-v1 up
      1.0, 1.0,   0.0, 1.0,   0.0, 0.0,   1.0, 0.0,    // v1-v6-v7-v2 left
      0.0, 0.0,   1.0, 0.0,   1.0, 1.0,   0.0, 1.0,    // v7-v4-v3-v2 down
      0.0, 0.0,   1.0, 0.0,   1.0, 1.0,   0.0, 1.0     // v4-v7-v6-v5 back
  ]);
  var indices = new Uint8Array([
     0, 1, 2,   0, 2, 3,    // front
     4, 5, 6,   4, 6, 7,    // right
     8, 9,10,   8,10,11,    // up
    12,13,14,  12,14,15,    // left
    16,17,18,  16,18,19,    // down
    20,21,22,  20,22,23     // back
  ])
  var o = new Object();  // 创建“Object”对象以返回多个对象。
  o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
  o.texCoordBuffer = initArrayBufferForLaterUse(gl, texCoords, 2, gl.FLOAT);
  o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
  o.numIndices = indices.length;
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  return o;
}

function initVertexBuffersForPlane(gl) { // 平面数据
  // 创建一个平面
  //  v1------v0
  //  |        | 
  //  |        |
  //  |        |
  //  v2------v3
  var vertices = new Float32Array([
    1.0, 1.0, 0.0,  -1.0, 1.0, 0.0,  -1.0,-1.0, 0.0,   1.0,-1.0, 0.0    // v0-v1-v2-v3
  ]);
  var texCoords = new Float32Array([1.0, 1.0,   0.0, 1.0,   0.0, 0.0,   1.0, 0.0]);
  var indices = new Uint8Array([0, 1, 2,   0, 2, 3]);
  var o = new Object(); // Create the "Object" object to return multiple objects.
  o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
  o.texCoordBuffer = initArrayBufferForLaterUse(gl, texCoords, 2, gl.FLOAT);
  o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
  o.numIndices = indices.length;
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  return o;
}

function initArrayBufferForLaterUse(gl, data, num, type) { // 顶点、纹理专用
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  buffer.num = num;
  buffer.type = type;
  return buffer;
}

function initElementArrayBufferForLaterUse(gl, data, type) { // 顶点索引专用
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW);
  buffer.type = type;
  return buffer;
}

function initTextures(gl) {
  var texture = gl.createTexture();
  var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
  var image = new Image(); 
  image.onload = function() {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);  // 翻转图像Y坐标
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    gl.uniform1i(u_Sampler, 0);
    gl.bindTexture(gl.TEXTURE_2D, null); // 取消绑定纹理对象
  };
  image.src = '../resources/sky_cloud.jpg';
  return texture;
}

function initFramebufferObject(gl) {
  var framebuffer, texture, depthBuffer;
  framebuffer = gl.createFramebuffer();   // 创建帧缓冲区对象(FBO)
  // 创建纹理对象并设置其大小和参数
  texture = gl.createTexture(); // 创建纹理对象
  gl.bindTexture(gl.TEXTURE_2D, texture); // 绑定纹理对象
  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; // 存储纹理对象

  // 创建渲染缓冲对象并设置其大小和参数
  depthBuffer = gl.createRenderbuffer(); // 创建渲染缓冲区对象
  gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); // 绑定渲染缓冲区对象
  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); // 设置渲染缓冲区大小

  /* 将 纹理对象、渲染缓冲对象 关联到帧缓冲区对象 */
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); // 绑定帧缓冲区
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); // 将帧缓冲区的颜色关联对象指定为一个纹理对象
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer); // 将帧缓冲区的深度关联对象指定为一个渲染缓冲区对象

  // 检查帧缓冲区对象是否正确配置
  var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
  if (gl.FRAMEBUFFER_COMPLETE !== e) console.log('Frame buffer object is incomplete: ' + e.toString());
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.bindTexture(gl.TEXTURE_2D, null);
  gl.bindRenderbuffer(gl.RENDERBUFFER, null);
  return framebuffer;
}
function draw(gl, canvas, fbo, plane, cube, angle, texture, viewProjMatrix, viewProjMatrixFBO) {
  gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);              // 绑定帧缓冲区对象,这样就可以在帧缓冲区中进行绘制
  gl.viewport(0, 0, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); // 指定绘图区域的左上角和宽高
  gl.clearColor(0.2, 0.2, 0.4, 1.0); // 颜色被轻微改变
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);  // 清除帧缓冲区中的颜色关联对象和深度关联对象(类似清除颜色缓冲区和深度缓冲区)
  drawTexturedCube(gl, gl.program, cube, angle, texture, viewProjMatrixFBO);   // 绘制立方体

  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.DEPTH_BUFFER_BIT); // 清除颜色缓冲区和深度缓冲区
  drawTexturedPlane(gl, gl.program, plane, angle, fbo.texture, viewProjMatrix);  // 绘制平面
}

// Coordinate transformation matrix
var g_modelMatrix = new Matrix4();
var g_mvpMatrix = new Matrix4();

function drawTexturedCube(gl, program, o, angle, texture, viewProjMatrix) {
  // 计算模型矩阵
  g_modelMatrix.setRotate(20.0, 1.0, 0.0, 0.0);
  g_modelMatrix.rotate(angle, 0.0, 1.0, 0.0);
  // 模型 * viewProjMatrix
  g_mvpMatrix.set(viewProjMatrix);
  g_mvpMatrix.multiply(g_modelMatrix);
  gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);

  drawTexturedObject(gl, program, o, texture);
}

function drawTexturedPlane(gl, program, o, angle, texture, viewProjMatrix) {
  // 计算模型矩阵
  g_modelMatrix.setTranslate(0, 0, 1);
  g_modelMatrix.rotate(20.0, 1.0, 0.0, 0.0);
  g_modelMatrix.rotate(angle, 0.0, 1.0, 0.0);
  // 模型 * viewProjMatrix
  g_mvpMatrix.set(viewProjMatrix);
  g_mvpMatrix.multiply(g_modelMatrix);
  gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);

  drawTexturedObject(gl, program, o, texture);
}

function drawTexturedObject(gl, program, o, texture) {
  // 分配缓冲区对象并启用分配
  initAttributeVariable(gl, program.a_Position, o.vertexBuffer);    
  initAttributeVariable(gl, program.a_TexCoord, o.texCoordBuffer);  

  // 将纹理对象绑定到目标
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // Draw
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, o.indexBuffer);
  gl.drawElements(gl.TRIANGLES, o.numIndices, o.indexBuffer.type, 0);
}

function initAttributeVariable(gl, a_attribute, buffer) { // 分配缓冲区对象并启用分配
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0);
  gl.enableVertexAttribArray(a_attribute);
}

var ANGLE_STEP = 30;   // 旋转角度的增量(度)
var last = Date.now(); // 上次调用此函数的时间
function animate(angle) {
  var now = Date.now();
  var elapsed = now - last;
  last = now;
  var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0; // 更新当前旋转角度(根据经过的时间进行调整)
  return newAngle % 360;
}

如下显示了示例程序FramebufferObject.js中上述第1步到第7步的代码。 

示例程序绘制了一个立方体和一个矩形。和WebGL 切换着色器-CSDN博客ProgramObject.js中一样,程序为立方体和矩形各创建了若干个缓冲区以存储顶点数据,并将缓冲区对象保存在Object类型的对象cube和plane中。这些缓冲区对象将在真正进行绘制前分配给着色器中的attribute变量。 

main()函数首先调用了initFramebufferObject()函数并新建了一个帧缓冲区对象fbo(第38行),然后将其作为参数传给draw()函数(第51行)。我们稍后再讨论draw()函数中的内容,先来逐步地研究一下initFramebufferObject()函数中的内容(第155行)。该函数实现了上述的第1步到第7步。注意,我们单独定义了帧缓冲区对象的视图投影矩阵(第44行),因为绘制立方体时的视图投影矩阵与绘制矩形时的并不一样。

创建帧缓冲区对象(gl.createFramebuffer())

在使用帧缓冲区对象之前,必须先创建它(第157行)。

gl.createFramebuffer()规范 

gl.createFramebuffer()函数可以创建帧缓冲区对象。

gl.deleteFramebuffer()规范 

gl.deleteFramebuffer()函数来删除一个帧缓冲区对象。 

创建出帧缓冲区对象后,还需要将其颜色关联对象指定为一个纹理对象,将其深度关联对象指定为一个渲染缓冲区对象。首先,我们来创建一个纹理对象。 

创建纹理对象并设置其尺寸和参数

在WebGL 纹理——在矩形表面贴上图像_山楂树の的博客-CSDN博客已经讨论过如何创建纹理对象,如何设置纹理对象的参数(gl.TEXTURE_MIN_FILTER)。注意,本例将纹理的宽度和长度分别存储在了OFFSETSCREEN_WIDTH和OFFSETSCREEN_HEIGHT中。我们将纹理的尺寸设置得比<canvas>略小一些,以加快绘制的速度。

gl.texImage2D()函数可以为纹理对象分配一块存储纹理图像的区域,供WebGL在其中进行绘制(第161行)。调用该函数时,将最后一个参数设为null,就可以新建一块空白的区域。WebGL 纹理——在矩形表面贴上图像_山楂树の的博客-CSDN博客中这个参数是传入的纹理图像Image对象。将创建出的纹理对象存储在framebuffer.texture属性上,以便稍后访问(第163行)。 

这样,我们就完成了第2步——创建纹理对象并设置其尺寸和参数。接着来创建渲染缓冲区对象。

创建渲染缓冲区对象(gl.createRenderbuffer())

在使用渲染缓冲区对象之前,我们必须先创建它(第166行)。

gl.createRenderbuffer()规范

调用gl.createRenderbuffer()函数创建渲染缓冲区对象。 

gl.deleteRenderbuffer() 规范

同样,可以使用gl.deleteRenderbuffer()函数来删除渲染缓冲区对象。

创建出来的渲染缓冲区对象将被指定为帧缓冲区的深度关联对象,我们将其保存在depthbuffer变量中。 

绑定渲染缓冲区并设置其尺寸(gl.bindRenderbuffer(),gl.renderbufferStorage()) 

在使用创建出的渲染缓冲区之前,还需要先将其绑定到目标上,然后通过对目标做一些额外的操作来设置渲染缓冲区的尺寸等参数。

我们使用gl.bindRenderbuffer()函数绑定渲染缓冲区。 

gl.bindRenderbuffer()规范

绑定完成后,我们就可以使用gl.renderbufferStorage()函数来设置渲染缓冲区的格式、宽度、高度等。注意,作为深度关联对象的渲染缓冲区,其宽度和高度必须与作为颜色关联对象的纹理缓冲区一致。

gl.renderbufferStorage()规范

 这样,我们就准备好了纹理对象和渲染缓冲区。接下来,我们就需要将它们关联到帧缓冲区上,并进行离屏绘图。

将纹理对象关联到帧缓冲区颜色关联对象(gl.bindFramebuffer(),gl.framebufferTexture2D()) 

使用帧缓冲区对象的方式与使用渲染缓冲区类似:先将缓冲区绑定到目标上,然后通过操作目标来操作缓冲区对象,而不能直接操作缓冲区对象。 

首先,调用gl.bindFramebuffer()函数绑定帧缓冲区对象。 

gl.bindFramebuffer()规范

 一旦帧缓冲区对象被绑定到target目标上,就可以通过target来使之与纹理对象进行关联。本例中,我们用一个纹理对象来代替颜色缓冲区,所以就将这个纹理对象指定为帧缓冲区的颜色关联对象。

调用gl.framebufferTexture2D()来完成这个任务。

 

注意,attachment参数的取值之一gl.COLOR_ATTACHMENT0,其名称中出现了一个0。这是因为在OpenGL中,帧缓冲区可以具有多个颜色关联对象(gl.COLOR_ATTACHMENT0、gl.COLOR_ATTACHMENT1等等),但是WebGL中只可以有1个。 

现在我们已经把纹理对象指定为帧缓冲区的颜色关联对象了,下面来把渲染缓冲区对象指定为帧缓冲区的深度关联对象。其过程是类似的。 

将渲染缓冲区对象关联到帧缓冲区对象(gl.framebufferRenderbuffer()) 

使用gl.framebufferRenderbuffer()函数来把渲染缓冲区对象关联到帧缓冲区对象上。这里,渲染缓冲区对象的作用是帮助进行隐藏面消除,所以我们将其指定为深度关联对象。

gl.framebufferRenderbuffer()规范

 现在,我们已经完成了帧缓冲区上的所有关联操作,只等WebGL在其中进行绘制了。但是在此之前,先检查一下帧缓冲区是否真的正确配置了。

检查帧缓冲区的配置(gl.checkFramebufferStatus()) 

显然,如果帧缓冲区对象没有被正确配置,就会发生错误。如你所见,前几节为帧缓冲区关联纹理对象和渲染缓冲区对象,它们的过程很复杂,有时会出现错误。我们可以使用gl.checkFramebufferStatus()函数来进行检查。 

gl.checkFramebufferStatus()规范 

 这样,我们就完成了对帧缓冲区的检查。下面来看一下draw()函数。

在帧缓冲区进行绘图

如下显示了draw()函数的代码。该函数首先把绘制目标切换为帧缓冲区对象fbo,并在其颜色关联对象,即在纹理对象中绘制了立方体。然后,我们把绘制目标切换回<canvas>,调用drawTextureddPlane()函数在颜色缓冲区中绘制矩形,同时把上一步在纹理对象中绘制的图像贴到矩形表面上。 

 首先调用gl.bindFramebuffer()函数绑定帧缓冲区对象,这样gl.drawArrays()和drawElements()函数就会在帧缓冲区中进行绘制了(第184行)。接着调用gl.viewport()函数定义离线绘图的绘图区域。

gl.viewport()规范

然后,清除帧缓冲区的中的颜色关联对象和深度关联对象(第187行),就像我们清除颜色缓冲区和深度缓冲区一样。接着绘制了立方体,其纹理是一幅蓝天白云的图像(第188行)。我们将背景色从黑色改成了紫蓝色,以突出显示矩形。这样,绘制在纹理缓冲区中的立方体就可以被当作纹理图像贴到矩形上去。接下来绘制矩形plane,这时需要在颜色缓冲区中绘制了,所以还得把绘制目标切换回来。调用gl.bindFramebuffer()函数并将第2个参数指定为null,解除了帧缓冲区的绑定(第190行),然后调用drawTexturedPlane()函数绘制了矩形(第194行)。注意,我们将存储了离屏绘制结果的纹理对象fbo.texture作为参数传入了该函数,供绘制矩形时使用。运行示例程序,你会发现矩形的正反两个表面都被贴上了纹理,这是因为WebGL默认绘制图形的正反两个表面(虽然你同时只能看到一个)。我们可以使用gl.enable(gl.CULL_FACE)来开启消隐功能(culling function),让WebGL不再绘制图形的背面,以提高绘制速度(理想情况下达到两倍)。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1049029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows:批处理bat入门

文章目录 什么是BAT常用命令与语法help与/?titlecolormodeechopausecallremset/a/p gotostartifif errorlevel for普通用法for /l 用法for /d用法for /r用法for /f用法in (file)delims和tokensskipeolusebackq 变量扩展变量延迟 setlocalshiftdirrd(删除文件夹&…

云中网络的隔离GREVXLAN

底层的物理网络设备组成的网络我们称为 Underlay 网络,而用于虚拟机和云中的这些技术组成的网络称为 Overlay 网络,这是一种基于物理网络的虚拟化网络实现。 第一个技术是 GRE,全称 Generic Routing Encapsulation,它是一种 IP-o…

Xposed 替换Textview文案

Xposed 替换Textview文案 这篇主要写下替换Textview的文案,主要实现比如脱敏。 public class TextViewHook {private static final String TAG "TextViewHook";public static void hook(XC_LoadPackage.LoadPackageParam lpparam) {Log.i(TAG, "ho…

取得信息系统项目管理师证书,薪资待遇怎么样?

作为信息系统项目管理师,薪资待遇是大家关心的一个话题。在中国,信息系统项目管理师是一种相对新兴的职业,但随着信息化时代的到来,这个职业的需求也越来越大。那么,信息系统项目管理师的薪资待遇到底怎么样呢&#xf…

高防服务器给企业带来的优势有哪些?

高防服务器主要指的是能够提供给网络安全提供高防护的服务器,通过流量清洗、负载均衡等手段来抵御DDoS攻击、CC攻击这一类流量攻击,为企业提供了强大的数据保障,互联网时代数据安全是放在第一位的,数据泄漏的话不论对于企业还是对…

国庆周《Linux学习第二课》

Linux开篇指南针环境安装(第一课)-CSDN博客 Linux详细的环境安装介绍在上面 第一 环境准备过程 安装过程

PS与PL与PG082

参考(照抄自己加点): ZYNQ PS-PL数据交互方式总结(好文)_axi emc-CSDN博客 zynq_process是一个用于方便操作PS和PL通信的GUI。 MIO分配在bank0和bank1直接与PS部分相连,EMIO分配在bank2直接和PL部分…

【每日一题】2703. 返回传递的参数的长度

2703. 返回传递的参数的长度 - 力扣(LeetCode) 请你编写一个函数 argumentsLength,返回传递给该函数的参数数量。 示例 1: 输入:args [5] 输出:1 解释: argumentsLength(5); // 1只传递了一个值…

odoo16 取消“系统各功能状态日报”的邮件

odoo16默认情况下每周都会发送一个“系统各功能状态日报”的邮件,而且是所有人都发, 这个功能在哪配置呢? 今天研究了一下, 线索是“系统各功能状态日报”,先全文检索吧 #. module: digest #: model:digest.digest,na…

windows:批处理bat实例

文章目录 文件/文件夹管理实例批量更改文件名创建编号从0到9的10个文件自动循环运行某个程序显示批处理的完整路径信息将文件名更名为当前系统日期使用批处理命令自动接收用户输入的信息计算当前目录及子目录(中文件)所占硬盘空间自动删除当前目录及子目…

【Spring Cloud系列】Config详解与应用

【Spring Cloud系列】Config详解与应用 文章目录 【Spring Cloud系列】Config详解与应用一、概述二、Config组成三、Spring Cloud Config 工作原理3.1 原理图3.2 Spring Cloud Config的原理 四、如何使用Spring Cloud Config4.1 创建Config Server4.2 创建Config Client4.3 配置…

2022年软件设计师下半年真题解析(上午+下午)

1 RISC 以下关于RISC(精简指令集计算机)特点的叙述中,错误的是()。 A.对存储器操作进行限制,使控制简单化B.指令种类多,指令功能强 C.设置大量通用寄存器 D.选取使用频率较高的一些指令,提高执行速度 RISC(Reduced Instruction Se…

计算机毕业设计 基于SSM的高校毕业论文管理系统小程序的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻…

力扣每日一题(+日常水题|树型dp)

740. 删除并获得点数 - 力扣(LeetCode) 简单分析一下: 每一个数字其实只有2个状态选 or 不 可得预处理每一个数初始状态(不选为0,选为所有x的个数 * x)累加即可 for(auto &x : nums)dp[x][1] x;每选一个树 i 删去 i 1 和 i - 1 故我们可以将 i…

MySQL8.0版安装教程 + Workbench可视化配置教程(史上最细、一步一图解)

文章目录 一、安装MySQL1、选择版本,点击“Download”进行下载2、双击下载好的安装包,点击运行3、选择安装类型为“Custom”4、依次进行选择,选到MySQL Servers 8.0.33 -X64,点击向右的箭头5、选中MySQL Servers 8.0.33 -X64&…

Allure-pytest功能特性介绍

前言 学pytest就不得不说fixture,fixture是pytest的精髓所在,就像unittest中的setup和teardown一样,如果不学fixture那么使用pytest和使用unittest是没什么区别的(个人理解)。 fixture用途 1.做测试前后的初始化设置,如测试数据准…

TikTok海外扩张:亚马逊的新对手崛起

随着社交媒体和电子商务的融合,TikTok正迅速崭露头角,成为亚马逊等传统电商巨头的潜在竞争对手。这一新兴平台的快速发展引发了广泛的关注,特别是在全球范围内。 在这篇文章中,我们将探讨TikTok海外扩张的战略,以及它…

26606-2011 工业用氰乙酸甲酯 阅读笔记

声明 本文是学习GB-T 26606-2011 工业用氰乙酸甲酯. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了工业用氰乙酸甲酯的要求、试验方法、检验规则、标志、包装、运输、贮存和安全。 本标准适用于以氯乙酸、氰化钠、甲醇等为原料…

uniapp h5 端 router.base设置history后仍有#号

manifest.json文件设置: "h5": { "router": { "base": "./", "mode": "history" }, }按相对路径发行时路由模式强制为hash模式,不支持history模式(两者相悖)…

FatFS文件系统在MCU上的应用

FatFS文件系统是单片机领域有名的一个文件系统,由于它的轻量级和兼容性,备受MCU开发者青睐。 在实现如U盘文件读写,SD卡的文件读写等工作时,我们往往需要一个文件系统来支持我们的工作。特别在一些MCU应用中,文件系统…