注意:文章翻译http://wgld.org/。原做者杉本雅広(doxas),文章中假设有个人额外说明,我会加上[lufy:]。另外,鄙人webgl研究还不够深刻。一些专业词语,假设翻译有误,欢迎你们指正。javascript
这是本次的demo的运行结果java
此次终于该绘制多边形了,以前的文章(十一,着色器的编译和链接)中介绍了HTML,顶点着色器和片断着色器,此次看一下javascript从開始到终于的全部处理。web
假设前两篇文章介绍的内容全然理解的话,此次的内容也应该不难了。也许会有不easy理解的地方,不要着急。
首先,我先贴出全部代码,而后在慢慢说明。
canvas
>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 = gl.getAttribLocation(prg, 'position'); // attribute的元素数量(此次仅仅使用 xyz ,因此是3) var attStride = 3; // 模型(顶点)数据 var vertex_position = [ 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; // 生成VBO var vbo = create_vbo(vertex_position); // 绑定VBO gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 设定attribute属性有効 gl.enableVertexAttribArray(attLocation); // 加入attribute属性 gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0); // 使用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 mvpMatrix = m.identity(m.create()); // 视图变换坐标矩阵 m.lookAt([0.0, 1.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, mvpMatrix); m.multiply(mvpMatrix, mMatrix, mvpMatrix); // uniformLocation的获取 var uniLocation = gl.getUniformLocation(prg, '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; } };
那么,从上到下按顺序来看一下吧。
首先,前提是在页面载入的同一时候运行script.js中的全部代码。因此。将代码全都写进了onload函数中。浏览器
以后,获取canvas对象開始处理。缓存
>canvas的获取和初始化例如如下
ide
// 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);最早開始作的是获取canvas对象,并设定canvas的大小为宽300px,高300px。而后获取WebGL的context,以及设定清除画面所使用的颜色。
相同。clear函数中的參数也作了对应的变化,不光是颜色,还包括深度。因此添加了gl.DEPTH_BUFFER_BIT常量。
函数
生成顶点着色器和片断着色器。并使用程序对象进行链接。在曾经的文章(着色器的编译和链接)中进行过具体的说明。可以參考一下。webgl
>着色器和程序对象相关的处理
// 顶点着色器和片断着色器的生成 var v_shader = create_shader('vs'); var f_shader = create_shader('fs'); // 程序对象的生成和链接 var prg = create_program(v_shader, f_shader); // attributeLocation的获取 var attLocation = gl.getAttribLocation(prg, 'position'); // attribute的元素数量(此次仅仅使用 xyz ,因此是3) var attStride = 3;注意这里出现的函数。并不是WebGL中内置的函数,而是本身写的。具体点就是说create_shader和create_program都是本身写的函数。
create_shader函数的參数是HTML中的id字符串,从script标签中获取到着色器代码,生成着色器对象并返回。
上面的样例中。是依据id是vs和fs的这两个script标签中的内容。来生成顶点着色器和片断着色器。
着色器生成后,做为參数传给create_program函数,返回值就是程序对象,而后用变量保存起来。在create_program函数中,生成程序对象,并将着色器进行链接。
接着。定义了两个变量,attLocation和attStride,用来保存在后面向顶点着色器中传入数据时的必要的内容。
话说回来,看一下曾经的文章中的代码中是怎样使用生成的着色器的。而后就明确为何要定义这两个变量了吧。
>使用顶点着色器的代码
attribute vec3 position; uniform mat4 mvpMatrix; void main(void){ gl_Position = mvpMatrix * vec4(position, 1.0); }此次使用着色器的时候。仅仅使用了一个attribute变量,固然就是position了,这个position变量定义成了vec3。说明是拥有3各元素的向量。
这里的重点就是[利用一个叫作position的attribute变量]和[这个变量是vec3类型]这两点内容。
事实上利用attribute变量向着色器中传递数据的时候,必要的两个情报是这个数据是第几个attribute变量,以及这个变量由几个元素组成的。
也就是说。变量attLocation是为了保存这个数据是第几个,变量attStride是为了保存这个数据是由几个元素组成的。
WebGL的context中的getAttribLocation函数的两个參数,第一个參数是程序对象。第二个參数是想要获取的attribute变量的变量名。返回值是数值型,就是向顶点着色器传递数据时的序号。就是告诉咱们是第几个变量。这个数值后面会使用到。
变量attStride表示顶点着色器中的attribute变量position是一个有三个元素的vec3类型的变量。这个后面也会用到。
继续吧,接着是定义模型数据。并生成VBO,而后为了将VBO传给顶点着色器,进行绑定并传入数据。
>VBO生成的相关处理
// 模型(顶点)数据 var vertex_position = [ 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; // 生成VBO var vbo = create_vbo(vertex_position); // 绑定VBO gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 设定attribute属性有効 gl.enableVertexAttribArray(attLocation); // 加入attribute属性 gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);曾经的文章( 十二。模型数据和顶点属性)中也有过具体说明。将顶点数据定义为简单的数组,而后依据这个数组数据。用本身定义函数create_vbo来生成顶点缓存(VBO)。
为了将顶点缓存和顶点着色器中的attribute变量联系起来,首先要向WebGL中绑定VBO。而后使用刚才获取到的attribute属性的序号。将这个attribute属性设定为有效。使用WebGL的函数enableVertexAttribArray可以让指定属性变为有效。
接着。使用WebGL函数vertexAttribPointer向着色器中写入数据。
刚才定义的两个变量attLocation和attStride。在这里也用到了。
vertexAttribPointer函数的第一个參数是attribute变量的序号,第二个參数是元素数。第三个參数是指定了数据类型的内置常量。gl.FLOAT是一个表示浮点型的常量。第四~第六各參数基本上是不怎么变的,依据内存有时候会传入其它的内容。
需要注意的是,运行vertexAttribPointer的时候。VBO对象必须先进行绑定。哪一个VBO以及和它关联的attribute属性是必须的,因此不要忘了先将VBO跟WebGL进行绑定。
接着,是准备渲染用的坐标变换矩阵。这里使用了本站点制做的矩阵计算的库minMatrix.js。minMatrix.js的主要的用法在上一篇文章中(minMatrix.js和坐标变换矩阵)已经介绍过了。
>坐标变换矩阵的生成以及相关的处理
// 使用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 mvpMatrix = m.identity(m.create()); // 视图变换坐标矩阵 m.lookAt([0.0, 1.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, mvpMatrix); m.multiply(mvpMatrix, mMatrix, mvpMatrix); // uniformLocation的获取 var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix'); // 向uniformLocation中传入坐标变换矩阵 gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);此次,模型变换矩阵初始化以后没有作不论什么处理就直接使用了,固然,并不是说不能操做模型变换矩阵,现在就先这么使用了。
>>uniform系列函数相关
此次出现的uniformMatrix4fv仅仅是uniform系列函数的一个。uniform系列函数有很是多种,主要分为下面几大类。
uniform 系列
uniform系列有uniform1 ~ uniform4,分别在向顶点着色器中传入一个到四各元素时使用。依据传给顶点着色器的数据的类型为整型或者浮点型,数字的后面会加上i(int)或者f(float)等小写字母。比方。传给着色器的数据是有两个元素的浮点型数据的时候,使用uniform2f。
演示样例 : gl.uniform2f(uniformLocation, date1, data2);
uniform v 系列
这一系列。基本上和上面的uniform系列没有太大区别,传入的数据是数组的时候使用。
和uniform系列同样,有1 ~ 四、数据类型也是在后面加入i或者f。
演示样例 : gl.uniform3iv(uniformLocation, Array);
uniformMatrix 系列
看名字就应该知道了。这个系列是在矩阵的时候使用。
固然,矩阵不可能出现1,因此仅仅有2 ~ 4。而且,矩阵中基本上仅仅使用浮点型小数、因此,数据类型也不存在i。数据就是以使用数组为前提的。因此后面加不加v都是没有区别的。
演示样例 : gl.uniformMatrix4fv(uniformLocation, false, Matrix);
着色器,顶点数据,坐标变换矩阵等,各类准备工做都完工了,终于该写绘制命令了。
>绘制命令和刷新
// 绘制模型 gl.drawArrays(gl.TRIANGLES, 0, 3); // context的刷新 gl.flush();运行WebGL的drawArrays函数的话,模型就被绘制到了缓存中了,这里之因此说是[缓存中],是因为当运行drawArrays函数的时候,尚未把多边形绘制到画面上。
这里出现的drawArrays函数,第一个參数是指定怎样使用顶点进行画图的一个常量,第二个參数是从第几个顶点開始使用,第三个參数是绘制几个顶点。
这一次,使用的是gl.TRAIANGLES,因此顶点被当成了纯粹的三角形多边形。利用三个顶点进行了绘制。
此次的文章有点太长了,贴出的代码量有点多。可能有人会吃了一惊吧。
事实上此次的代码绘制的仅仅是一个简单的三角形。仅仅是这样。却写了这么长的代码,因此才说3D开发是比較难的。
但是。我的以为,即便这样。和DirectX相比較的话,已经至关简单,简练了。
单从开发环境上来讲。不需要特别的开发环境,WebGL这一点已经很是轻松了吧。理解了本次的内容的话,仅仅需要略微慢慢的调整一下。就可以实现很是多效果。这之后的内容,都是以此次的内容为基础的,因此必需要好好的理解一下。
本文章最后,给出本文demo的连接,假设浏览器支持的话。直接看demo比較直观吧。
那么。下次给多边形进行着色,敬请期待。
渲染三角形的demo