注意:文章翻译http://wgld.org/。原作者杉本雅広(doxas),文章中假设有我的额外说明,我会加上[lufy:],另外。鄙人webgl研究还不够深入。一些专业词语。假设翻译有误。欢迎大家指正。
本次的demo的执行结果
顶点缓存的反复利用
上一篇文章中,向多边形的三个顶点中加入了颜色这个新的顶点属性,给多边形涂抹了颜色。并且知道。使用新的VBO能够给对顶点属性进行自由的扩张。
那么。这次来挑战一下同一时候绘制多个模型。可是,不准备新的VBO。还利用上次的VBO。就是说,反复利用VBO,仅仅操作坐标变换矩阵,来绘制多个模型。
所以,这次着色器相关的内容全然不变。全然使用上次的顶点着色器和片段着色器,javascript代码变更的地方也不多,下面重点说一下变更的部分。
坐标变换矩阵的反复利用
这次绘制多个模型。坐标变换矩阵相关也是反复利用。
也就是说。在实际中。将多个模型绘制到不同的位置的时候,必须要操作的坐标变换矩阵仅仅有模型变换矩阵,而决定镜头的位置的视图变换矩阵和决定屏幕范围的投影变换矩阵这两个坐标变换矩阵,都能够使用同一个。
过程例如以下,依照下面这样操作矩阵的话就能够了。
・准备好视图和投影两个坐标变换矩阵
・提前将两个矩阵相乘并保存起来(下面、pv)
・准备第一个模型坐标变换矩阵(下面、m1)
・m1和pv相乘,传给uniform
・绘制第一个模型
・准备第二个模型坐标变换矩阵(下面、m2)
・m2和pv相乘,传给uniform
・绘制第二个模型
・刷新context
主要是。视图坐标变换矩阵和投影坐标变换矩阵提前相乘并保存起来。然后等模型坐标变换矩阵准备好之后。再与之前的结果相乘,传给uniform,然后画图。
那么。来看一下实际的代码吧。
>script.js中的部分代码
//各种矩阵的生成和初始化 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, 3.0], [0, 0, 0], [0, 1, 0], vMatrix); m.perspective(90, c.width / c.height, 0.1, 100, pMatrix); m.multiply(pMatrix, vMatrix, tmpMatrix); //移动第一个模型的模型坐标变换矩阵 m.translate(mMatrix, [1.5, 0.0, 0.0], mMatrix); //模型x视图x投影(第一个模型) m.multiply(tmpMatrix, mMatrix, mvpMatrix); //将坐标变换矩阵传入uniformLocation,并画图(第一个模型) gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); //移动第二个模型的模型坐标变换矩阵 m.identity(mMatrix); m.translate(mMatrix, [-1.5, 0.0, 0.0], mMatrix); //模型x视图x投影(第二个模型) m.multiply(tmpMatrix, mMatrix, mvpMatrix); //将坐标变换矩阵传入uniformLocation,并画图(第二个模型) gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); //刷新context gl.flush();重点是,为了临时保存视图和投影坐标变换矩阵,新声明了一个tmpMatrix这种用法。使用matIV.multiply将视图坐标变换矩阵和投影坐标变换矩阵相乘,然后结果保存到tmpMatrix中。
然后,使用matIV.translate来操作模型坐标变换矩阵,将第一个模型向X方向移动1.5,然后将这个模型坐标变换矩阵和tmpMatrix相乘。传给uniform并进行画图。
第二个模型的坐标矩阵,和之前相反,向X方向上移动-1.5,然后和第一个一样,和tmpMatrix相乘。传给uniform并进行画图。
最后,刷新context,在canvas上绘制两个多边形。
这样。利用VBO和一部分坐标变换矩阵。省去了不必要的代码,绘制了两个多边形。
须要注意的是。对第二个模型准备模型坐标变换矩阵的时候,首先在最初的时候使用matIV.identity对矩阵进行初始化。
假设,不进行初始化直接使用matIV.translate的话。会受前一回移动的影响,导致结果改变。
进行完初始化之后再进行操作,是为了避免相似这种发生错误。
附加:attribute属性加入的函数化
上次的文章中也涉及到了,添加顶点属性的时候,假设将对应的处理函数化的话能够提高效率,尽管和这次的主题(绘制多个模型)没有关系,可是由于demo中进行了函数化。所以简单的讲解一下。
>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); } }这个函数有三个參数。三个參数都是数组。使用for ~ in来循环VBO中的属性,并进行绑定和加入。
一般,须要准备非常多个VBO,所以将这里函数化,以后能够省略非常多代码了吧。
总结
这次操作的是模型坐标变换矩阵,介绍了反复利用VBO。视图和投影坐标变换矩阵。进行多个模型的绘制的方法。
绘制非常多个简单的模型,图形的时候,像这次的做法一样。能够使处理变的简洁一些。避免写非常多多余的代码。
这次的demo,HTML的代码和上一次是全然一样的,就是说。顶点着色器和片段着色器没有做不论什么调整。
javascript代码有了一些变化,所以贴出所有代码。另外,在文章的最后面,加入了demo的链接。有支持WebGL的浏览器的话。能够直接打开链接看一下效果。
下次,进一步操作模型坐标变换矩阵,对绘制的模型进行各种各样的处理。
>script.js的所有代码
onload = function(){ // canvas对象获取 var c = document.getElementById('canvas'); c.width = 300; c.height = 300; // webgl的context获取 var gl = c.getContext('webgl') || c.getContext('experimental-webgl'); // 设定canvas初始化的颜色 gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设定canvas初始化时候的深度 gl.clearDepth(1.0); // canvas的初始化 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 顶点着色器和片段着色器的生成 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 vertex_position = [ 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; // 保存顶点的颜色情报的数组 var vertex_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 ]; // 生成VBO var pos_vbo = create_vbo(position); var col_vbo = create_vbo(color); // 将VBO进行绑定并加入 set_attribute([pos_vbo, col_vbo], attLocation, attStride); // 获取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, 3.0], [0, 0, 0], [0, 1, 0], vMatrix); m.perspective(90, c.width / c.height, 0.1, 100, pMatrix); m.multiply(pMatrix, vMatrix, tmpMatrix); //移动第一个模型的模型坐标变换矩阵 m.translate(mMatrix, [1.5, 0.0, 0.0], mMatrix); //模型x视图x投影(第一个模型) m.multiply(tmpMatrix, mMatrix, mvpMatrix); //将坐标变换矩阵传入uniformLocation,并画图(第一个模型) gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); //移动第二个模型的模型坐标变换矩阵 m.identity(mMatrix); m.translate(mMatrix, [-1.5, 0.0, 0.0], mMatrix); //模型x视图x投影(第二个模型) m.multiply(tmpMatrix, mMatrix, mvpMatrix); //将坐标变换矩阵传入uniformLocation,并画图(第二个模型) gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); //刷新context gl.flush(); // 生成着色器的函数 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); } } };
绘制多个模型的demo