开始WebGL程序之前,还是得进行一些理论上的知识,不然会让自己的代码写得不明不白。上一篇文章就是写得关于WebGL的一些基础知识http://nbcoders.com/detail/156
第一:首先获取WebGL上下文。
第二:编写顶点着色器
第三:开始一些自己的绘制工作(调用WebGL API)
这里我们就以画一个三角形为例子,开始我们的WegGL程序吧。
首先获取WebGL的上下文环境,也就是渲染环境,我们要利用HTML5的canvas元素。所以我们这里要新建一个Canvas元素,这里canvas可以对应一个WebGL的渲染环境,也可以对应一个canvas-2d的渲染环境。编写如下代码:
<html> <body> <canvas id="webglCanvas" style="border:1px solid blue;" width='600px' height='600px'></canvas> </body> </html>
可以简单运行试试效果,有个蓝色的细框。接下来我们要获取WebGL的渲染区域,然后操作WebGL渲染区域。这里通过canvas的getContext方法获取WebGL的上下文环境。
var webglContext = document.getElementById().getContex("experimental-webgl");
这里完整代码如下:
function initCanvas(canvasId) { var canvas = document.getElementById(canvasId); var context = null; try{ context = canvas.getContext("experimental-webgl"); }catch(ex) { alert(ex.toString()); } if(!context) { alert("我靠,你的浏览器不支持WebGL,换个浏览器吧!"); return null; } return context; }获取了绘图上下文之后,接下来就是要开始写shader程序和绘制相关代码了。在编写Shader代码前,先简单回顾介绍一下着色器。着色器有两种,顶点着色器和片段着色器。简单点的来说,顶点着色器就是负责处理顶点的位置,片段着色器就是用来处理顶点的颜色。前面已经介绍过顶点着色器和片段着色器,废话就不多说了,来点演示的。顶点着色器和片段着色器的shader代码如下:
<span style="white-space:pre"> </span><script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 v3Position; void main(void) { gl_Position = vec4(v3Position, 1.0); } </script> <script id="shader-fs" type="x-shader/x-fragment"> void main(void) { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } </script>上面一段代码只是shader程序,要创建着色器还是要通过WebGL API,这里着色器用 createShader函数创建,该函数的参数只有一个,用于控制创建着色器的类型,是一个枚举类型。顶点着色器用 VERTEX_SHADER表示,片段着色器用 FRAGMENT_SHADER表示,最后函数返回一个创建好的对象。再来解释一下 上面那段shader代码。当我们创建好着色器之后,我们还需要将3d内容转换成要绘制显示的内容。Shader程序用字符串来表示的,所以,这里写了一个解析器来解析Shader代码,将其转换。
//解析Shader代码 function shaderSourceFromScript(scriptID) { var shaderScript = document.getElementById(scriptID); if (shaderScript == null) return ""; var sourceCode = ""; var child = shaderScript.firstChild; while (child) { if (child.nodeType == child.TEXT_NODE) sourceCode += child.textContent; child = child.nextSibling; } return sourceCode; }当然,这里我们并不能说就完事了,因为我们只是刚好解析好Shader代码,接下来的事情是要对Shader代码进行编译和链接。着色器的编译源码的WebGL Api是compileShader(shaderObject),链接API为attachShader(programObject, shaderObject),这里也不对这个函数进行细说了,查查就知道。编译和链接的代码如下:
//编译shader程序 function compileShader(context, shaderVertexCode, shaderFragmentCode, vertexShaderobject, fragmentShaderObject) { //将shader代码装载到shader Object中 context.shaderSource(vertexShaderobject, shaderVertexCode); context.shaderSource(fragmentShaderObject, shaderFragmentCode); //编译shader代码 context.compileShader(vertexShaderobject); context.compileShader(fragmentShaderObject); //检查是否编译成功 if (!context.getShaderParameter(vertexShaderobject, context.COMPILE_STATUS)) { alert("error:vertexShaderObject"); return; } if (!context.getShaderParameter(fragmentShaderObject, context.COMPILE_STATUS)) { alert("error:framentShaderObject"); return; } } //链接Shader程序 function linkShader(context, programObj, vertexShaderObj, fragmentShaderObj) { //一个程序对象只能并且必须附带一个顶点着色器和片段着色器 context.attachShader(programObj, vertexShaderObj); context.attachShader(programObj, fragmentShaderObj); //将着色器变量关联到一个属性索引 context.bindAttribLocation(programObj, v3PositionIndex, "v3Position"); context.linkProgram(programObj); //检查是否链接成功 if (!context.getProgramParameter(programObj, context.LINK_STATUS)) { alert("error:ProgramObject"); return; } return programObj; } //初始化Shader程序 function initShader(context) { //创建shaderobject vertexShaderObj = context.createShader(context.VERTEX_SHADER); fragmentShaderObj = context.createShader(context.FRAGMENT_SHADER); //编译shader compileShader(context, shaderSourceFromScript("shader-vs"), shaderSourceFromScript("shader-fs"), vertexShaderObj, fragmentShaderObj); //链接shader //创建一个程序对象 programObj = context.createProgram(); programObj = linkShader(context, programObj, vertexShaderObj, fragmentShaderObj); //context指定使用shader程序 context.useProgram(programObj); }经过这步之后,Shader程序就已经编译和链接好了。接下来,我们要想渲染出我们想要的图形来,我们还需要向WebGL提供我们相应的数据。所以我们接下来的操作是建立缓冲,用来保存顶点数据。这里的第一个程序,只需要保存一个三角形的顶点位置,因为基本每一句我都写了注释,所以,直接上代码:
//初始化顶点数据 function initVextexData(context) { //顶点坐标 var jsArrayData = [ 0.0, 1.0, 0.0, -1.0, 0.0, -1.0, 1.0, 0.0, 0.0 ] //创建一个webgl能够访问的缓冲 var triangleBuffer = context.createBuffer(); //绑定buffer context.bindBuffer(context.ARRAY_BUFFER, triangleBuffer); //将js数据拷贝到buffer上 context.bufferData(context.ARRAY_BUFFER, new Float32Array(jsArrayData), context.STATIC_DRAW); return triangleBuffer; }好吧,还有一步,绘制场景(即绘制所以对象)
function start() { //初始化webgl渲染区域 webglContext = initCanvas("webglCanvas"); //初始化shader程序 initShader(webglContext); //初始化顶点数据 triangleBuffer = initVextexData(webglContext); //开始绘制 //清空屏幕 webglContext.clearColor(0.0, 0.0, 0.0, 1.0); webglContext.clear(webglContext.COLOR_BUFFER_BIT); //webgl中顶点数组数据可能N个,我们这里需要告诉webgl我们用哪一个, //绑定一个顶点数组数据 webglContext.bindBuffer(webglContext.ARRAY_BUFFER, triangleBuffer); //启动关联索引上的数据 webglContext.enableVertexAttribArray(v3PositionIndex); //指定关联索引上的数据元素或者元素数据的正确信息 webglContext.vertexAttribPointer(v3PositionIndex, 3, webglContext.FLOAT, false, 0, 0); //绘制数据 webglContext.drawArrays(webglContext.TRIANGLES, 0, 3); }最后全部的代码如下:
<html> <head> <script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 v3Position; void main(void) { gl_Position = vec4(v3Position, 1.0); } </script> <script id="shader-fs" type="x-shader/x-fragment"> void main(void) { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } </script> <script type="text/javascript"> var webglContext = null; var vertexShaderObj = null; var fragmentShaderObj = null; var programObj = null; var v3PositionIndex = null; var triangleBuffer = null; //解析Shader代码 function shaderSourceFromScript(scriptID) { var shaderScript = document.getElementById(scriptID); if (shaderScript == null) return ""; var sourceCode = ""; var child = shaderScript.firstChild; while (child) { if (child.nodeType == child.TEXT_NODE) sourceCode += child.textContent; child = child.nextSibling; } return sourceCode; } //编译shader程序 function compileShader(context, shaderVertexCode, shaderFragmentCode, vertexShaderobject, fragmentShaderObject) { //将shader代码装载到shader Object中 context.shaderSource(vertexShaderobject, shaderVertexCode); context.shaderSource(fragmentShaderObject, shaderFragmentCode); //编译shader代码 context.compileShader(vertexShaderobject); context.compileShader(fragmentShaderObject); //检查是否编译成功 if (!context.getShaderParameter(vertexShaderobject, context.COMPILE_STATUS)) { alert("error:vertexShaderObject"); return; } if (!context.getShaderParameter(fragmentShaderObject, context.COMPILE_STATUS)) { alert("error:framentShaderObject"); return; } } //链接Shader程序 function linkShader(context, programObj, vertexShaderObj, fragmentShaderObj) { //一个程序对象只能并且必须附带一个顶点着色器和片段着色器 context.attachShader(programObj, vertexShaderObj); context.attachShader(programObj, fragmentShaderObj); //将着色器变量关联到一个属性索引 context.bindAttribLocation(programObj, v3PositionIndex, "v3Position"); context.linkProgram(programObj); //检查是否链接成功 if (!context.getProgramParameter(programObj, context.LINK_STATUS)) { alert("error:ProgramObject"); return; } return programObj; } function initShader(context) { //创建shaderobject vertexShaderObj = context.createShader(context.VERTEX_SHADER); fragmentShaderObj = context.createShader(context.FRAGMENT_SHADER); //编译shader compileShader(context, shaderSourceFromScript("shader-vs"), shaderSourceFromScript("shader-fs"), vertexShaderObj, fragmentShaderObj); //链接shader //创建一个程序对象 programObj = context.createProgram(); programObj = linkShader(context, programObj, vertexShaderObj, fragmentShaderObj); //context指定使用shader程序 context.useProgram(programObj); } //初始化canvas function initCanvas(canvasId) { var canvas = document.getElementById(canvasId); var context = null; try{ context = canvas.getContext("experimental-webgl"); }catch(ex) { alert(ex.toString()); } if(!context) { alert("我靠,你的浏览器不支持WebGL,换个浏览器吧!"); return null; } //获得了绘图上下文之后,设置视口 context.viewport(0, 0, canvas.width, canvas.height); return context; } //初始化顶点数据 function initVextexData(context) { //顶点坐标 var jsArrayData = [ 0.0, 1.0, 0.0, -1.0, 0.0, -1.0, 1.0, 0.0, 0.0 ] //创建一个webgl能够访问的缓冲 var triangleBuffer = context.createBuffer(); //绑定buffer context.bindBuffer(context.ARRAY_BUFFER, triangleBuffer); //将js数据拷贝到buffer上 context.bufferData(context.ARRAY_BUFFER, new Float32Array(jsArrayData), context.STATIC_DRAW); return triangleBuffer; } function start() { //初始化webgl渲染区域 webglContext = initCanvas("webglCanvas"); //初始化shader程序 initShader(webglContext); //初始化顶点数据 triangleBuffer = initVextexData(webglContext); //开始绘制 //清空屏幕 webglContext.clearColor(0.0, 0.0, 0.0, 1.0); webglContext.clear(webglContext.COLOR_BUFFER_BIT); //webgl中顶点数组数据可能N个,我们这里需要告诉webgl我们用哪一个, //绑定一个顶点数组数据 webglContext.bindBuffer(webglContext.ARRAY_BUFFER, triangleBuffer); //启动关联索引上的数据 webglContext.enableVertexAttribArray(v3PositionIndex); //指定关联索引上的数据元素或者元素数据的正确信息 webglContext.vertexAttribPointer(v3PositionIndex, 3, webglContext.FLOAT, false, 0, 0); //绘制数据 webglContext.drawArrays(webglContext.TRIANGLES, 0, 3); } </script>> </head> <body onload="start()"> <canvas id = "webglCanvas" style = "border:1px solid blue" width = "600px" height = "600px"></canvas> </body> </html>运行效果如下: