注:文章译自http://wgld.org/。原作者杉本雅広(doxas),文章中假设有我的额外说明。我会加上[lufy:]。另外,鄙人webgl研究还不够深入,一些专业词语,假设翻译有误,欢迎大家指正。

本次的demo的执行结果

相应复杂的模型

上次,通过操作模型坐标变换矩阵,实现了多个模型的移动,旋转和放大缩小。可是。渲染的依旧是简单的三角形,是个构造及其简单的模型。
可是实际中,用WebGL来绘制一个简单的三角形的机会是非常少见的。至少是个四角多边形吧,一般会是更复杂的模型。
伴随着模型的复杂化,顶点的个数也会大幅度添加,越是精密圆滑的模型,所须要的顶点的量也会变的肥大化。javascript中倒是没有什么限制。也不是为了节省内存或者是节省代码量,直接绘制这些大量顶点数据也不是说不能够,可是尽可能的降低数据量这样的想法是程序猿本性。
在WebGL中,有一种方法能够防止顶点数据的肥大化,是使用索引缓存来管理顶点数据。索引缓存又叫IBO(index buffer objact)。是缓存的一种。并且。如今为止一直使用的相似的缓存还有VBO,IBO一般和VBO一起使用。

顶点的添加

像刚才说的一样,顶点添加的越多,管理的顶点数据就越庞大。可是,能够使用反复使用少量的顶点来形成多边形。

来看一下详细的样例吧。

多边形是由三角形组成的。比方一个矩形就是由两个三角形组成的。


那么,这时候须要几个顶点呢?
看刚才的图的话,须要四个顶点。而定义一个三角形须要三个顶点。两个多边形的话。按说是应该须要六个顶点的吧。
顶点的坐标情报是全然相同的。假设反复的顶点多次被定义的话,明显是浪费的。

所以。应该考虑顶点的反复利用。

反复的顶点的再利用,尽可能降低顶点数据,这时候顶点缓存就出现了。顶点缓存,是用来保存顶点的索引情报的。

像刚才的图一样,第一个多边形使用的是[ 1, 2, 3 ]这三个顶点,相同,第二个多边形使用的是[ 2, 3, 4 ]这三个顶点。
这样,绘制多边形的时候告诉WebGL使用哪些顶点的就是顶点索引,这次的四边形效果还不够明显,而越是巨大的复杂的模型,使用索引缓存的作用就会越大。
使用索引缓存,除了节省代码量之外,还有另外非常大的优点。

使用索引缓存的话,能够直接使用GPU的内存,不用每次画图都向GPU传递数据,这样速度会更快。

技能节省代码量,又实现快速化,所以没有理由不使用IBO。

IBO的生成

索引缓存的生成步骤。和顶点缓存相似,尽管有几个參数变化。可是基本上是相同的流程。
这次的demo新定义了一个create_ibo函数,来看一下代码吧。
>自己定义函数create_ibo

// IBO的生成函数
function create_ibo(data){
// 生成缓存对象
var ibo = gl.createBuffer(); // 绑定缓存
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); // 向缓存中写入数据
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW); // 将缓存的绑定无效化
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); // 返回生成的IBO
return ibo;
}

和生成VBO的时候一样。首先使用createBuffer函数来生成空的缓存对象,然后绑定缓存,bindBuffer函数的第一个參数是一个和VBO不一样的内置常量 gl.ELEMENT_ARRAY_BUFFER,指定这个常量生成的缓存就是IBO。

然后绑定缓存并写入数据。VBO的时候。传入的參数的是Float32Array型数据,可是索引缓存使用的不是浮点型小数,而是整数,所以这里使用的是Int16Array型数据。
这个自己定义函数create_ibo,传入的參数是一个数组,和生成VBO的函数create_vbo是一样的。

IBO所用的数组数据和VBO一样。都是预定义好的。
>索引数据的数组定义的样例

var index = [
0, 1, 2,
1, 2, 3
];

有四个顶点的时候,像上面写的一样。第一个三角形使用顶点[ 0, 1, 2 ],第二个三角形使用顶点[ 1, 2, 3 ]来绘制的意思。顶点的索引是从0開始的,须要注意,数组是一维数组。

画图相关的变更点

了解了索引缓存的生成。接着看一下索引缓存的使用步骤。
使用IBO进行渲染,须要提前将IBO和WebGL进行绑定,用以下的代码能够实现。
>IBO绑定的代码

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);

传入刚才生成的IBO(变量ibo),这样就完毕了IBO的绑定。可是光是绑定IBO,是没办法进行画图的,还须要改动一下画图命令。

到如今为止,渲染多边形一直是使用drawArrays函数。为了使用IBO进行画图。须要使用drawElements函数。这个函数的使用样例例如以下。
>使用drawElements函数的画图命令举例

gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);

这个drawElements函数有四个參数,第一个參数是表示渲染如何的顶点的常量。第二个參数是索引缓存的元素数,第三个參数是索引数据的数据大小,第四个參数是索引数据的起始索引offset。
比較难理解的是第二个參数。这里的话,传入保存索引数据的数组长度的话就能够了,其它的參数没什么特殊限制的话。不做改动就这么使用的话即可了。

总结

使用索引缓存IBO。既能够节约数据,又能够提高渲染顶点的效率,并且。模型越复杂影响越大,这是程序猿非常乐意看到的。

索引缓存和顶点缓存一样,能够使用纯粹的一维数组来生成。生成的IBO进行绑定后就能够使用了,利用IBO画图的时候使用drawElements函数是重点。
这次的demo。顶点着色器和片段着色器是没有不论什么变化的,所以,还使用曾经的HTML代码,javascript的内容多少有些变化,最后会给出完整代码,另外。文章最后会贴出本次demo的链接。能够參考一下。

下次,会介绍一下遮挡剔除和深度。

>script.js的完整代码

onload = function(){
// canvas对象获取
var c = document.getElementById('canvas');
c.width = 500;
c.height = 300; // webgl的context获取
var gl = c.getContext('webgl') || c.getContext('experimental-webgl'); // 顶点着色器和片段着色器的生成
var v_shader = create_shader('vs');
var f_shader = create_shader('fs'); // 程序对象的生成和连接
var prg = create_program(v_shader, f_shader); // attributeLocation的获取
var attLocation = new Array(2);
attLocation[0] = gl.getAttribLocation(prg, 'position');
attLocation[1] = gl.getAttribLocation(prg, 'color'); // 将元素数attribute保存到数组中
var attStride = new Array(2);
attStride[0] = 3;
attStride[1] = 4; // 保存顶点的颜色情报的数组
var position = [
0.0, 1.0, 0.0,
1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
0.0, -1.0, 0.0
];
// 保存顶点的颜色情报的数组
var color = [
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0
]; // 保存顶点的索引的数组
var index = [
0, 1, 2,
1, 2, 3
]; // 生成VBO
var pos_vbo = create_vbo(position);
var col_vbo = create_vbo(color); // 将VBO进行绑定并加入
set_attribute([pos_vbo, col_vbo], attLocation, attStride); // 生成IBO
var ibo = create_ibo(index); // IBO进行绑定并加入
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); // uniformLocationの取得
var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix'); // 使用minMatrix.js对矩阵的相关处理
// matIV对象生成
var m = new matIV(); // 各种矩阵的生成和初始化
var mMatrix = m.identity(m.create());
var vMatrix = m.identity(m.create());
var pMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create()); // 视图x投影坐标变换矩阵
m.lookAt([0.0, 0.0, 5.0], [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(45, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix); // 定义计数器
var count = 0; // 持续循环
(function(){
// canvasを初期化
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 计数器递增
count++; // 使用计数器算出角度
var rad = (count % 360) * Math.PI / 180; // 模型坐标变换矩阵的生成(沿着Y轴旋转)
m.identity(mMatrix);
m.rotate(mMatrix, rad, [0, 1, 0], mMatrix);
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); // 使用索引进行画图
gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0); // context刷新
gl.flush(); // 为了循环,进行递归处理
setTimeout(arguments.callee, 1000 / 30);
})(); // 生成着色器的函数
function create_shader(id){
// 用来保存着色器的变量
var shader; // 依据id从HTML中获取指定的script标签
var scriptElement = document.getElementById(id); // 假设指定的script标签不存在,则返回
if(!scriptElement){return;} // 推断script标签的type属性
switch(scriptElement.type){ // 顶点着色器的时候
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break; // 片段着色器的时候
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default :
return;
} // 将标签中的代码分配给生成的着色器
gl.shaderSource(shader, scriptElement.text); // 编译着色器
gl.compileShader(shader); // 推断一下着色器是否编译成功
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){ // 编译成功,则返回着色器
return shader;
}else{ // 编译失败,弹出错误消息
alert(gl.getShaderInfoLog(shader));
}
} // 程序对象的生成和着色器连接的函数
function create_program(vs, fs){
// 程序对象的生成
var program = gl.createProgram(); // 向程序对象里分配着色器
gl.attachShader(program, vs);
gl.attachShader(program, fs); // 将着色器连接
gl.linkProgram(program); // 推断着色器的连接是否成功
if(gl.getProgramParameter(program, gl.LINK_STATUS)){ // 成功的话,将程序对象设置为有效
gl.useProgram(program); // 返回程序对象
return program;
}else{ // 假设失败,弹出错误信息
alert(gl.getProgramInfoLog(program));
}
} // 生成VBO的函数
function create_vbo(data){
// 生成缓存对象
var vbo = gl.createBuffer(); // 绑定缓存
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 向缓存中写入数据
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); // 将绑定的缓存设为无效
gl.bindBuffer(gl.ARRAY_BUFFER, null); // 返回生成的VBO
return vbo;
} // 绑定VBO相关的函数
function set_attribute(vbo, attL, attS){
// 处理从參数中得到的数组
for(var i in vbo){
// 绑定缓存
gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]); // 将attributeLocation设置为有效
gl.enableVertexAttribArray(attL[i]); //通知并加入attributeLocation
gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0);
}
}
// IBO的生成函数
function create_ibo(data){
// 生成缓存对象
var ibo = gl.createBuffer(); // 绑定缓存
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); // 向缓存中写入数据
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW); // 将缓存的绑定无效化
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); // 返回生成的IBO
return ibo;
} };

利用索引缓存(IBO)画图的demo

http://wgld.org/s/sample_006/

转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend

最新文章

  1. [leetcode] 题型整理之动态规划
  2. python 随笔(property & __class__)
  3. [磁盘管理与分区]——关于分区、磁盘分区表、MBR
  4. mysql5.6子查询的优化
  5. sql2008“备份集中的数据库备份与现有数据库不同”解决方法
  6. Linux内核学习笔记2
  7. HTML5速查表
  8. 用Karma和Jasmine测试Angular应用
  9. python自动化测试
  10. 将txt文档按行分割
  11. BZOJ 3864 Hero meet devil 超详细超好懂题解
  12. 使用Holer远程桌面登录家里电脑和公司内网电脑
  13. Jquery ajax传递复杂参数给WebService
  14. 【BZOJ2137】submultiple 高斯消元求伯努利数
  15. python's default parameter
  16. 关于 warning CS0659:“***”重写Object.Equals(object o)但不重写Object.GetHashCode()
  17. python:webbrowser
  18. Count On A Tree II.
  19. Spring Boot中JPA如何实现按日期合计
  20. 【Linux/Ubuntu学习 11】git查看某个文件的修改历史

热门文章

  1. 【leetcode】solution in java——Easy1
  2. linux 命令行cd dvd iso操作
  3. Apache禁止显示目录结构
  4. 2、redis原生的命令操作不同数据类型
  5. excel文字格式设置
  6. Servlet学习(一)
  7. ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
  8. 转:高层游戏引擎——基于OGRE所实现的高层游戏引擎框架
  9. Android 本地播放器
  10. Struts1与Struts2的那些事