注:文章译自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

[WebGL入门]十八,利用索引缓存来画图的更多相关文章

  1. [WebGL入门]十九,遮挡剔除和深度測试

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

  2. [WebGL入门]十四,绘制多边形

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

  3. [WebGL入门]十六,绘制多个模型

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

  4. [WebGL入门]十二,模型数据和顶点属性

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

  5. [WebGL入门]十五,为多边形涂抹颜色(顶点颜色的指定)

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

  6. android 项目学习随笔十八(三级缓存)

    xUtils的BitmapUtils模块用的就是三级缓存,在项目中尽量还是应用BitmapUtils 三级缓存(机制) import com.itheima.zhsh66.R; import andr ...

  7. [WebGL入门]十,矩阵计算和外部库

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

  8. Django框架(二十八)—— Django缓存机制

    目录 Django缓存机制 一.什么是缓存 二.Django的6中缓存方式及配置(只需要改配置文件) 1.开发调试缓存(此模式为开发调试使用,实际上不执行任何操作) 2.内存缓存(将缓存内容保存至内存 ...

  9. [译]Kinect for Windows SDK开发入门(十八):Kinect Interaction交互控件

    本文译自 http://dotneteers.net/blogs/vbandi/archive/2013/03/25/kinect-interactions-with-wpf-part-i-getti ...

随机推荐

  1. 解决 ERROR in native method: JDWP No transports initialized, jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197)

    在/etc/hosts文件中加入下面一行内容 127.0.0.1 localhost.localdomain localhost

  2. Jenkins 关闭和重启

    关闭jenkins服务:http://localhost:8080/exit 将上面的exit改为restart后就可以重新启动jenkins服务器.http://localhost:8080/res ...

  3. Android插件化开发之解决OpenAtlas组件在宿主的注冊问题

    OpenAtlas有一个问题,就是四大组件必须在Manifest文件里进行注冊,那么就必定带来一个问题,插件中的组件都要反复在宿主中注冊.像Service,ContentProvider等组件眼下没有 ...

  4. 转:典型开源3D引擎分类比较

    常见的3D引擎有:Unreal.Quake.Lithtech.OGRE.Nebula.Irrlicht.Truevision3D... 其中开源免费的有:OGRE.irrlicht.fly3d.Neo ...

  5. poj 2059 单调栈

    题意:求柱状图中最大矩形面积. 单调栈:顾名思义就是栈内元素单调递增的栈. 每次插入数据来维护这个栈,假设当前须要插入的数据小于栈顶的元素,那就一直弹出栈顶的元素.直到满足当前须要插入的元素大于栈顶元 ...

  6. rox + openbox + fbpanel + conky打造又快又稳的桌面

    从开始用 Gentoo 以来,就没有打算用 gnome . KDE 这些巨无霸级别的 DE ,最后选择了相对来来说比较轻量级的 Xfce4 ,不过最近更是变本加厉,连 Xfce4 都觉得太大.于是,下 ...

  7. JSP弹出对话框方式小结

    转自:http://blog.csdn.net/ithomer/article/details/8033002 该博主(创业)另一博客地址: http://blog.mimvp.com JSP 网页在 ...

  8. JavaScript调用App原生代码(iOS、Android)通用解决方案

    实际场景 场景:现在有一个H5活动页面,上面有一个登陆按钮,要求点击登陆按钮以后,唤出App内部的登录界面,当登录成功以后将用户的手机号返回给H5页面,显示出来.这个场景应该算是比较完整的一次H5中的 ...

  9. iOS 中的各种锁

    在日常开发过程中,为了提升程序运行效率,以及用户体验,我们经常使用多线程.在使用多线程的过程中,难免会遇到资源竞争问题.我们采用锁的机制来确保线程安全. 线程安全 当一个线程访问数据的时候,其他的线程 ...

  10. c运行库、c标准库、windows API的区别和联系

    C运行时库函数C运行时库函数是指C语言本身支持的一些基本函数,通常是汇编直接实现的.  API函数API函数是操作系统为方便用户设计应用程序而提供的实现特定功能的函数,API函数也是C语言的函数实现的 ...