【WebGL初学系列之二】WebGL第一个程序,三角形

时间:2022-05-20 04:15:14

   开始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>
运行效果如下:

【WebGL初学系列之二】WebGL第一个程序,三角形

  地址:http://nbcoders.com/detail/164