【笔记】《WebGL编程指南》学习(9)

WebGL编程指南学习(9)

Ragnarok!

9. 高级技巧(续)

α\alphaα混合

如何实现α\alphaα混合

两个步骤

  1. 开启混合功能gl.enable(gl.BLEND)
  2. 指定混合函数gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

混合函数

gl.blendFund()在进行α\alphaα混合时,实际上用到了两个颜色,源颜色(source color)和目标颜色(destination color)。前者是“待被混合的颜色”,也就是原本的颜色;后者是“待被混合进去的颜色”。

gl.blendFunc(src_factor, dst_factor)
// 混合后颜色=源颜色*src_factor+目标颜色*dst_factor

这里的src_factor和dst_factor是有特指的,比如gl.ZERO,gl.ONE_MINUS_DST_COLOR

半透明的三维物体

  • α\alphaα混合发生在绘制片元的过程。当隐藏面消除功能开启时,被隐藏的片元不会被绘制,所以也就不会发生混合过程,更不会有半透明的效果。只要关闭隐藏面消除就可以达到半透明的效果
  • 但是关闭隐藏面消除功能只是一个粗暴的解决方案。实际绘制三维场景时,场景中往往既有不透明的物体,也有半透明的物体,如果关闭隐藏面消除功能,那些不透明物体的前后关系就会乱套

同时实现隐藏面消除和半透明效果的机制

  1. 开启隐藏面消除
  2. 绘制所有不透明的物体
  3. 锁定用于进行隐藏面消除的深度缓冲区的写入操作,使之只读
  4. 绘制所有半透明的物体,注意它们应当按照深度排序,然后从后向前绘制
  5. 释放深度缓冲区,使之可读可写
gl.depthMask(mask)
// 锁定或释放深度缓冲区的写入操作,true/false

由于深度缓冲区被锁定了,绘制不透明物体后面的物体,即使是半透明的也不会显示;而深度在不透明物体前面的,会正常显示不透明的效果

切换着色器

对不同的物体,经常需要使用不同的着色器来绘制。每个着色器中可能有非常复杂的逻辑以实现各种不同的效果。我们可以准备多个着色器,然后根据需要来切换它们

渲染到纹理

使用WebGL渲染三维图形,然后将渲染结果作为纹理贴到另一个三维物体上去。

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

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

帧缓冲区对象(framebuffer object)可以代替颜色缓冲区或深度缓冲区。绘制在帧缓冲区中的对象并不会直接显示在canvas上。因此,我们就可以先对帧缓冲区中的内容进行一些处理再显示;或者直接用其中的内容作为纹理图像。

在帧缓冲区中进行绘制的过程又称为离屏绘制(offscreen drawing)

帧缓冲区提供了颜色缓冲区和深度缓冲区的替代品——所关联的对象(attachment)。一个帧缓冲区有3个关联对象:颜色关联对象(color attachment)、深度关联对象(depth attachment)和模板关联对象(stencil attachment),分别用来替代颜色缓冲区、深度缓冲区和模板缓冲区

  • 经过一些设置,WebGL可以向帧缓冲区的关联对象写入数据
  • 每个关联对象又可以是两种类型的:纹理对象渲染缓冲区对象

如何实现渲染到纹理?

如果希望把WebGL渲染出的图像作为纹理使用,那么就需要:

  1. 将纹理对象作为颜色关联对象关联到帧缓冲区对象上;
  2. 在帧缓冲区中进行绘制

此时,颜色关联对象(即纹理对象)就替代了颜色缓冲区。由于此时仍然需要进行隐藏面消除,所以又创建一个渲染缓冲区对象来作为帧缓冲区的深度关联对象,以替代深度缓冲区

所以,最终帧缓冲区的配置是这样的——

颜色关联对象——纹理对象

深度关联对象——渲染缓冲区对象

注意这两个对象的绘图区域大小必须一致

例程:将一个旋转的立方体作为纹理贴在一个矩形上

算法步骤

  1. 创建帧缓冲区对象
  2. 创建纹理对象并设置其尺寸和参数
  3. 创建渲染缓冲区对象
  4. 绑定渲染缓冲区对象并设置其尺寸
  5. 将帧缓冲区的颜色关联对象指定为一个纹理对象
  6. 将帧缓冲区的深度关联对象指定为一个渲染缓冲区对象
  7. 检查帧缓冲区是否配置正确
  8. 在帧缓冲区进行绘制

前调

// 着色器
...
// 设置离屏绘制的尺寸
var OFFSCREEN_WIDTH = 256;
var OFFSCREEN_HEIGHT = 256;

function main() {
  // 一些上下文设置
  ...
  // 设置顶点信息
  var cube = initVertexBufferForCube(gl);
  var plane = initVertexBufferForPlane(gl);
  ...
  // 设置纹理
  var texture = initTextures(gl);
  ...
  // 初始化帧缓冲区(Framebuffer object,FBO)
  var fbo = initFramebufferObject(gl);
  ...
  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) {
  // 创建一个cube
}
  
function initVertexBuffersForPlane(gl) {
  // 创建一个face
}

function initArrayBufferForLaterUse(gl, data, num, type) {
  // 创建一个buffer object
}

function initElementArrayBufferForLaterUse(gl, data, type) {
  // 创建一个buffer object
} 

创建帧缓冲区对象、创建纹理对象并设置其尺寸和参数、创建渲染缓冲区对象并设置其尺寸和参数、将纹理对象关联到帧缓冲区

function initFramebufferObject(gl) {
  var frameBuffer, texture, depthBuffer;
  ...
  // 创建一个FBO
  framebuffer = gl.createFramebuffer();
  ...
  // 创建一个纹理对象并设置其尺寸和参数
  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.texOarameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  framebuffer.texture = texture; // 保存纹理对象
  // 创建一个渲染缓冲区对象(renderbuffer object),并设置其尺寸和参数
  depthBuffer = gl.createRenderbuffer();
  ...
  gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);
  // 把纹理和渲染缓冲区对象关联到FBO
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  gl.framebuferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
  // 检查FBO是否配置正确
  var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
  ...
}

渲染缓冲区的设置

gl.renderbufferStorage(target, internalformat, width, height);

其中,internalformat指定渲染缓冲区中的数据格式,包括gl.DEPTH_COMPONENT16(表示渲染缓冲区将替代深度缓冲区)、gl.STENCIL_INDEX8(表示渲染缓冲区将替代模板缓冲区)、gl.RGBA4(表示渲染缓冲区将替代颜色缓冲区),等

关联纹理对象到FBO

gl.framebufferTexture2D(target, attachment, textarget, texture, level);
// 参数:
// target:必须是gl.FRAMEBUFFER
// attachent: 
// 	gl.COLOR_ATTACHMENT0 颜色关联对象
//	gl.DEPTH_ATTACHMENT 深度关联对象
// textarget: 纹理类型gl.TEXTURE_2D|gl.TEXTURE_CUBE
// texture: 要关联的纹理对象
// level: 0, 使用mipmap纹理时指定纹理的层级

关联帧缓冲区对象到FBO

gl.framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
// 参数
// attachment:
//	gl.COLOR_ATTACHMENT0: 颜色关联对象
//	gl.DEPTH_ATTACHMENT:	深度关联对象
//	gl.STENCIL_ATTACHMENT:模板关联对象

在FBO进行绘图

  • 首先把绘制目标切换为FBO,并在其颜色关联对象,即纹理对象中绘制立方体;
  • 然后把绘制目标切换回canvas,在颜色缓冲区中绘制矩形,同时把上一步在纹理对象中绘制的图像贴到矩形表面上
function draw(gl, canvas, fbo, plane, cube, angle, texture viewProjMatrix, viewProjMAtrixFBO) {
  // 切换渲染目标为FBO
  gl.bindFrameBuffer(gl.FRAMEBUFFER, fbo);
  gl.viewport(0, 0, OFFSCREEN_WIDTH, OFFSCREEEN_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);
}

注意fbo.texture作为参数传入了drawTexturedPlane,供绘制平面是使用。fbo.texture是离屏绘制结果的纹理对象

基本信息 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: ()Kouichi Matsuda Rodger Lea(松田浩一,罗杰.李) 译者: 谢光磊 出版社:电子工业出版社 ISBN:9787121229428 上架时间:2014-6-11 出版日期:2014 年6月 开本:16开 页码:470 版次:1-1 ---------------------------------------- 目录 《WebGL编程指南》 第1 章 WebGL 概述 1 WebGL 的优势 3 使用文本编辑器开发三维应用 3 轻松发布三维图形程序 4 充分利用浏览器的功能 5 学习和使用WebGL 很简单 5 WebGL 的起源 5 WebGL 程序的结构 6 总结 7 第2 章 WebGL 入门 9 Canvas 是什么? 10 使用[canvas] 标签 11 DrawRectangle.js 13 最短的WebGL 程序:清空绘图区 16 HTML 文件(HelloCanvas.html) 16 JavaScript 程序(HelloCanvas.js) 17 用示例程序做实验 22 绘制一个点(版本1) 22 HelloPoint1.html 24 HelloPoint1.js 24 着色器是什么? 25 使用着色器的WebGL 程序的结构 27 初始化着色器 29 顶点着色器 31 片元着色器 33 绘制操作 34 WebGL 坐标系统 35 用示例程序做实验 37 绘制一个点(版本2) 38 使用attribute 变量 38 示例程序(HelloPoint2.js) 39 获取attribute 变量的存储位置 41 向attribute 变量赋值 42 gl.vertexAttrib3f() 的同族函数 44 用示例程序做实验 45 通过鼠标点击绘点 46 示例程序(ClickedPoints.js) 47 注册事件响应函数 48 响应鼠标点击事件 50 用示例程序做实验 53 改变点的颜色 55 示例程序(ColoredPoints.js) 56 uniform 变量 58 获取uniform 变量的存储地址 59 向uniform 变量赋值 60 gl.uniform4f() 的同族函数 61 总结 62 第3 章 绘制和变换三角形 63 绘制多个点 64 示例程序(MultiPoint.js) 66 使用缓冲区对象 69 创建缓冲区对象(gl.createBuffer()) 70 绑定缓冲区(gl.bindBuffer()) 71 向缓冲区对象中写入数据(gl.bufferData()) 72 类型化数组 74 将缓冲区对象分配给attribute 变量(gl.vertexAttribPointer()) 75 开启attribute 变量(gl.enableVertexAttribArray()) 77 gl.drawArrays() 的第2 个和第3 个参数 78 用示例程序做实验 79 Hello Triangle 80 示例程序(HelloTriangle.js) 80 基本图形 82 用示例程序做实验 83 Hello Rectangle(HelloQuad) 84 用示例程序做实验 85 移动、旋转和缩放 86 平移 87 示例程序(TranslatedTriangle.js) 88 旋转 91 示例程序(RotatedTriangle.js) 93 变换矩阵:旋转 97 变换矩阵:平移 100 4×4 的旋转矩阵 101 示例程序(RotatedTriangle_Matrix.js) 102 平移:相同的策略 105 变换矩阵:缩放 106 总结 108 第4 章 高级变换与动画基础 109 平移,然后旋转 109 矩阵变换库:cuon-matrix.js 110 示例程序(RotatedTriangle_Matrix4.js) 111 复合变换 113 示例程序(RotatedTranslatedTriangle.js) 115 用示例程序做实验 117 动画 118 动画基础 119 示例程序(RotatingTriangle.js) 119 反复调用绘制函数(tick()) 123 按照指定的旋转角度绘制三角形(dr
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值