WebGL使用了GLSL ES(OpenGL ES)着色器语言,经过配合调用js相关的绘制接口来实现3D效果。web
采用页面中的<canvas>元素来定义绘图区域,canvas支持三维图形的绘制,但它不直接提供绘图方法,而是提供一种叫上下文(context)机制来绘制图形。canvas
1 var canvas = document.getElementById('canvas'); 2 var gl = canvas.getContext('webgl');
具体的参数能够参考这里:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/getContext数组
1 // 着色器是字符串形式的glsl代码 2 var VSHADER_SOURCE = "..."; 3 var FSHADER_SOURCE = "...";
createShader方法,须要传递参数指明是顶点着色器仍是片断着色器;缓存
shaderSource方法,能够指定着色器代码添加到指定的Shader上;webgl
compileShader方法,能够将附加了着色器的shader对象进行编译;spa
createProgram方法,能够建立一个程序对象,一个可用的WebGLProgram对象由两个编译事后的WebGLShader组成,即顶点着色器和片断着色器;code
attachShader方法,能够把一个编译好的着色器对象添加到一个程序对象上;orm
linkProgram方法,链接一个程序对象,链接以后的程序对象可使用;对象
useProgram方法,设定一个程序对象为当前使用的程序对象;blog
根据须要调用对应的方法进行绘制;
示例代码点击这里;
绘制图像须要向GPU传递多个顶点,WebGL提供了一种很方便的机制,缓冲区对象(buffer object),它是WebGL系统中的一块内存区域,它能够一次性向着色器传入多个顶点的数据,而后将这些数据保存在其中,供顶点着色器使用。
绘制图像除了要完成上面说的步骤以外,还有下面的步骤须要完成:
createBuffer方法,建立缓冲区对象;
bindBuffer方法,绑定缓冲区对象,同时代表缓冲区的类型,以下:
bufferData方法,须要代表缓冲数据的类型,同上,还要代表数据是否会变更,以下:
getAttribLocation方法,获取指定属性的下标;
vertexAttribPointer方法,会告知到GPU的属性怎么获取传入的数据,参数分别以下:
enableVertexAttribArray方法,就是开启指定的变量,使缓存区对attribute变量分配生效,以便顶点着色器可以访问缓冲区内的数据。
使用attribute定义的属性都是能够接收外部传入的数据的属性,接收到的数据能够直接在着色器中使用;
1 attribute vec4 a_Position; 2 3 void main() { 4 gl_Position = a_Position; 5 }
drawArrays方法,用来绘制图像,参数分别以下:
第一个参数:绘制类型
三角形 gl.TRIANGLES 一系列单独的三角形,绘制(v0,v1,v2),(v3,v4,v5)……若是不是3的倍数,剩下的将会被忽略。
三角带 gl.TRIANGLES_STRIP 一系列链接的三角形,绘制(v0,v1,v2),(v2,v1,v3),(v2,v3,v4)……以此类推,第二个是(v2,v1,v3)而不是(v1,v2,v3)是为了保持绘制按照逆时针绘制
第二个参数:开始绘制的顶点索引
第三个参数:绘制的顶点数量
示例代码点击这里;
咱们但愿修改传输到GPU中的顶点的位置时(好比放大缩小旋转等),须要从外部传入一个矩阵来进行运算,外部传入的矩阵是一个包含16个数字的带数据类型的数组对象;
关于矩阵的相关教程能够看这里;
1 // 控制缩放的矩阵 2 var Sx = 1.5; Sy = 1.5; Sz = 1.0; 3 var xformMatrix = new Float32Array([ 4 Sx, 0.0, 0.0, 0.0, 5 0.0, Sy, 0.0, 0.0, 6 0.0, 0.0, Sz, 0.0, 7 0.0, 0.0, 0.0, 1.0 8 ]);
uniformMatrix4fv方法,传输到GPU,参数以下:
代码以下:
1 // 获取矩阵变量的下标 2 var u_xformMatarix = gl.getUniformLocation(shaderProgram, 'u_xformMatarix'); 3 // 提交矩阵数据到GPU 4 gl.uniformMatrix4fv(u_xformMatarix, false, xformMatrix);
1 attribute vec4 a_Position; 2 uniform mat4 u_xformMatarix; 3 4 void main() { 5 // 设置坐标 6 gl_Position = u_xformMatarix * a_Position; 7 }
示例代码点击这里;
咱们能够经过顶点数据向着色器添加颜色信息。
1 //顶点坐标和颜色 2 var vertices = new Float32Array([ 3 0.0, 0.5, 1.0, 0.0, 0.0, 4 -0.5, -0.5, 0.0, 1.0, 0.0, 5 0.5, -0.5, 0.0, 0.0, 1.0, 6 ]);
颜色是在片断着色器中处理的,可是顶点数据只在顶点着色器中可使用,因此咱们须要将顶点着色器中的部分数据传递到片断着色器中,使用varying定义的属性能够将顶点着色器中的数据传递到片断着色器中;
1 // 顶点着色器 2 attribute vec4 a_Position; 3 attribute vec4 a_Color; 4 // 声明须要传递到片断着色器的属性 5 varying vec4 v_Color; 6 7 void main() { 8 // 设置坐标 9 gl_Position = a_Position; 10 // 传递颜色数据 11 v_Color = a_Color; 12 } 13 14 15 // 片断着色器 16 // 必须在片断着色器中为float指定一个默认精度 17 precision mediump float; 18 // 声明从顶点着色器接收数据的属性 19 varying vec4 v_Color; 20 21 void main() { 22 gl_FragColor = v_Color; 23 }
1 //获取单个字节 2 var FSIZE = vertices.BYTES_PER_ELEMENT; 3 4 //获取坐标点 5 var a_Position = gl.getAttribLocation(shaderProgram, 'a_Position'); 6 7 //将缓冲区对象分配给a_Position变量 8 gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0); 9 10 //链接a_Position变量与分配给它的缓冲区对象 11 gl.enableVertexAttribArray(a_Position); 12 13 //获取Color坐标点 14 var a_Color = gl.getAttribLocation(shaderProgram, 'a_Color'); 15 16 //将缓冲区对象分配给a_Position变量 17 gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2); 18 19 //链接a_Position变量与分配给它的缓冲区对象 20 gl.enableVertexAttribArray(a_Color);
示例代码点击这里;
这里咱们看看怎么使用webgl来绘制图片;
经过建立Image对象,能够动态加载指定的图片,保存这个Image对象,后期直接使用这个对象提交到GPU便可;
1 //对纹理图像进行Y轴反转,由于WebGL纹理坐标系统的t轴(分为t轴和s轴)的方向和图片的坐标系统Y轴方向相反。所以将Y轴进行反转。 2 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); 3 4 //开启0号纹理单元,WebGL经过纹理单元的机制来同时使用多个纹理,gl.TEXTURE0~gl.TEXTURE7是管理纹理图像的8个纹理单元 5 gl.activeTexture(gl.TEXTURE0); 6 7 //向target绑定纹理对象 8 gl.bindTexture(gl.TEXTURE_2D, texture); 9 10 //配置纹理参数 11 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 12 13 //配置纹理图像 14 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); 15 16 //获取u_Sampler的存储位置 17 var u_Sampler = gl.getUniformLocation(shaderProgram, 'u_Sampler'); 18 19 //将0号纹理图像传递给着色器 20 gl.uniform1i(u_Sampler, 0); 21 22 // 清空 <canvas> 23 gl.clear(gl.COLOR_BUFFER_BIT); 24 25 // 绘制矩形 26 gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
Webgl中,是使用UV坐标来标记图像的采样点,UV的详情点击这里;
1 // 顶点着色器 2 attribute vec4 a_Position; 3 attribute vec2 a_TextCoord; 4 varying vec2 v_TexCoord; 5 6 void main() { 7 // 设置坐标 8 gl_Position = a_Position; 9 // 传递uv坐标 10 v_TexCoord = a_TextCoord; 11 } 12 13 // 片断着色器 14 precision mediump float; 15 // 声明采样器 16 uniform sampler2D u_Sampler; 17 varying vec2 v_TexCoord; 18 19 void main() { 20 // 设置颜色,经过采样得到指定坐标的颜色 21 gl_FragColor = texture2D(u_Sampler, v_TexCoord); 22 }
示例代码点击这里;
咱们以前是提交顶点数据,而后经过drawArrays来绘制三角形,咱们知道一个矩形是由两个三角形组合而成的,因此能够用下面的两种方式来进行绘制:
1 // 3个顶点为一组进行绘制,绘制一个矩形须要6个点 2 gl.drawArrays(gl.TRIANGLES, 0, 6); 3 4 // 传入4个顶点,按照(v0,v1,v2),(v2,v1,v3),(v2,v3,v4)...的顺序进行绘制 5 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
这里,第一种方法,会致使大量的重复顶点数据存在,第二种方法,虽然能够节省顶点数据,可是是规定好的顺序,不够自由;
为了解决这个问题,WebGL还引入了另一种绘制方法drawElements,这种方法须要传递两种数据,一种是顶点数据,一种是索引数据;
顾名思义,顶点数据定义一堆不一样的顶点,而索引数据定义这些顶点的绘制顺序;
1 //定义索引数组 2 var indexDatas = 3 [ 4 0, 1, 2, 5 2, 1, 3, 6 ];
1 //建立缓冲区对象 2 indexBuffer = gl.createBuffer(); 3 //将缓冲区对象绑定到目标,并设定该缓冲区是索引缓冲 4 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 5 //向缓冲区写入数据 6 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexDatas), gl.STATIC_DRAW);
不须要作任何的修改;
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
示例代码点击这里;