现在的位置: 首页 > 综合 > 正文

WebGL简单范例及代码

2013年09月15日 ⁄ 综合 ⁄ 共 5769字 ⁄ 字号 评论关闭

转自博客:http://viml.nchc.org.tw/blog/paper_info.php?CLASS_ID=1&SUB_ID=1&PAPER_ID=166

 

    在上一篇文章,算是很简略地介绍了 WebGL 以及目前能让 WebGL 正常运作的浏览器;而这一篇,就是来写一个最简单的 WebGL 程序了~在这个例子里,就是单纯地在黑底的框框内,画出一个白色的三角型(如右图);基本上是以类似 2D 的模式在画,也不会考虑到 3D 的投影。

 

    不过要先强调的是,要写 WebGL 的话,基本上应该要了解:

 

    JavaScript 与网页的 DOM 的操作

 

    OpenGL ES 2.0 的程序架构

 

    如果对 OpenGL ES 2.0 完全没碰过的话,至少也要对 OpenGL 3.0 或是 OpenGL 2.x + GLSL 有概念才行。

 

    如果都没有的话,建议先去稍微研究一下这些东西,对于学习 WebGL 会比较好。

 

    而对于一个 WebGL 的程序,Heresy 会把他分为三个部份:

 

    HTML 部分,包含要绘制内容的 canvas 组件

 

    WebGL JavaScript 程序部分

 

    WebGL 程序所需使用的 GLSL 程序的部分(包含 vertex shader fragment shader

 

    首先,先讲最简单的 HTML 的部分。由于这部分除了网页本身的内容外,就只是单纯地需要额外配置一个 <canvas>,来指定 WebGL 的区域,所以其实相当地单纯。下方就是一个很简单的例子:

 

    <body onload="RunWebGL();"> <canvas id="canvas_object" width="500" height="500"></canvas></body>这样写的话,就是会在网页里产生一个 id 是「canvas_object」、大小是 500 x 500 的画布,可以用来呈现 WebGL 的绘制结果。

 

    另外,这个网页也会在读取完成后,就自动执行 RunWebGL() 这个 JavaScript 的函式、开始执行 WebGL 的程序;这个函式的性质接近一般 C/C++ 程序的 main(),也就是前面所提的第二部分。他的内容如下:

 

    function RunWebGL(){ 

 

      // get WebGL Context 

         var canvas = document.getElementById( "canvas_object" ); 

         g_WebGLContext = canvas.getContext( "experimental-webgl" );   // setup viewport 

         g_WebGLContext.viewport(0, 0, canvas.width, canvas.height);  // set clear color 

         g_WebGLContext.clearColor( 0.0, 0.0, 0.0, 1.0 );   // create shader and data 

         CreateShader(); 

         CreateData();   // main loop 

        setInterval( drawScene, 30 );

 

      }

 

    而在这边比较特别的,就是在编写 WebGL 程序时,必须先透过 DOM 来取得要使用的 Canvas、并透过他产生 WebGL rendering context,也就是函式一开始标示成黄色的区块。

 

    其中,g_WebGLContext 是一个全域变量,是用来指向 WebGL rendering context 的。而在程序上,就是先透过 DOM 找到 id 为「canvas_object」的组件,再透过 canvas getContext() 来取得 WebGL rendering context;不过要稍微注意的是,目前是使用「experimental-webgl」,但是以后等到 WebGL 正式版的时候,应该会改成使用「webgl」。

 

    而由于这是一个很简单的范例,所以在取得 WebGL Context 后,就只有简单的设定 viewport clear color,其它都使用 OpenGL ES 的默认值,然后就开始建立所需的 shader program 和对象数据、然后进入 main loop 了~

 

    由于 OpenGL 3.0 / OpenGL ES 2.0 基本上已经将 fixed pipeline 的东西丢了,所以连同在 WebGL 环境里,都是一定要自己编写 Vertex Shader Fragment Shader 的!

 

    而在 WebGL 里,每一个 shader 程序,都是一个独立的 <script>、藉由不同的 id type 来做区隔;id 基本上就是自己取的、用来识别用的名称,而 type 则有「x-shader/x-vertex」和「x-shader/x-fragment」两种。

 

    下面就是一个简单的 vertex shader 的范例,他基本上不会做任何坐标的转换、投影,只会直接把每一个 vertex 的位置信息转换为 vec4 的型态,继续往下传:

 

    <script id="vs_01" type="x-shader/x-vertex"> 

 

       attribute vec3 aVertexPosition;  

       void main(void) 

 

       {   

 

       gl_Position = vec4( aVertexPosition, 1 ); 

 

       }

 

    </script>

 

    而下方则是一个简单的 fragment shader,他会把所有的 fragment 的颜色都填为白色:

 

   <script id="fs_01" type="x-shader/x-fragment"> 

 

       void main(void) 

      

       {    gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ); 

 

       }

 

    </script>

 

    在上面这样写了之后,基本上就是写好了最单纯的 vertex shader fragment shader 了。

 

    而要使用这两个 shader 程序,在 JavaScript 程序的部分还需要先透过 DOM 的架构,把他们的程序代码读取出来成一个字符串,这部分 Heresy 是把他写成一个函式:

 

    function getShaderSource( id )

 

      {  // get shader script element 

 

         var shaderScript = document.getElementById( id ); 

 

         if( !shaderScript )    return null;   // get shader program string 

 

         var str = "";  var childNode = shaderScript.firstChild; 

 

         while( childNode ) 

             {    

 

              if( childNode.nodeType == childNode.TEXT_NODE )     

              str += childNode.textContent;   

              childNode = childNode.nextSibling; 

 

                }

 

           return str;}

 

     这边的程序,会先找到指定 id element,然后依序地把他里面的 text node 的数据读出来,并且累加到 str 这个字符串里,最后再传出来。

 

     而在取得 shader 程序的程序代码之后,还需要再以 OpenGL shader 的处理程序,进行编译、连结等动作;这边也就是在 RunWebGL() 中呼叫到的 CreateShader() 函式了~

 

     1: function CreateShader() 

     2: { 

     3:   // create vertex shader 

     4:   var vsSource = getShaderSource( "vs_01" ); 

     5:   var vertexShader = g_WebGLContext.createShader( g_WebGLContext.VERTEX_SHADER ); 

     6:  

     7:   // compile vertex shader 

     8:   g_WebGLContext.shaderSource( vertexShader, vsSource ); 

     9:   g_WebGLContext.compileShader( vertexShader );

     10:   if( !g_WebGLContext.getShaderParameter( vertexShader, g_WebGLContext.COMPILE_STATUS ) )

     11:     alert( g_WebGLContext.getShaderInfoLog( vertexShader ) );

     12: 

     13:   // create fragment shader

     14:   var fsSource = getShaderSource( "fs_01" );

     15:   var fragmentShader = g_WebGLContext.createShader( g_WebGLContext.FRAGMENT_SHADER );

     16: 

     17:   // compile fragment shader

     18:   g_WebGLContext.shaderSource( fragmentShader, fsSource );

     19:   g_WebGLContext.compileShader( fragmentShader );

     20:   if( !g_WebGLContext.getShaderParameter( fragmentShader, g_WebGLContext.COMPILE_STATUS ) )

     21:     alert( g_WebGLContext.getShaderInfoLog( fragmentShader ) );

     22: 

     23:   // create shader program

     24:   g_ShaderProgram = g_WebGLContext.createProgram();

     25:   g_WebGLContext.attachShader( g_ShaderProgram, vertexShader );

     26:   g_WebGLContext.attachShader( g_ShaderProgram, fragmentShader );

     27:   g_WebGLContext.linkProgram( g_ShaderProgram );

     28: 

     29:   if( !g_WebGLContext.getProgramParameter( g_ShaderProgram, g_WebGLContext.LINK_STATUS ) )

     30:   {

     31:     alert( "Shader 初始化失败" );

     32:     return;

     33:   }

     34: 

     35:   g_WebGLContext.useProgram( g_ShaderProgram );

     36: }

 

     在这个函式里,就是各别针对 vertex shader fragment shader,依序地读取 shader 的程序代码、建立 shader 对象、编译 shader 了;而在这两个 shader object 都建立完成后,就再将这两个 shader object attach shader program(全域变量 g_ShaderProgram)上,并且 link、使用这个包含了 vs_01 以及 fs_01 shader program 了。而如果都没出问题的话,在执行完 CreateShader() 后,也就完成了 WebGL shader 的配置了。

 

     接下来,就是 CreateData()、也就是建立要画的三角型的部分了;这部分也是相当简单,就是建立一个 vertex array,里面放三个只有位置信息的 vertex 了~

 

     function CreateData()

     { 

        g_VertexPositionAttribute = g_WebGLContext.getAttribLocation( g_ShaderProgram, "aVertexPosition" ); 

        g_WebGLContext.enableVertexAttribArray( g_VertexPositionAttribute );   // create scene data 

        g_VertexPositionBuffer = g_WebGLContext.createBuffer(); 

        g_WebGLContext.bindBuffer( g_WebGLContext.ARRAY_BUFFER, g_VertexPositionBuffer ); 

        var vertices = [   0.0,  0.8, 0.0,                    -0.8, -0.8, 0.0,                     0.8, -0.8, 0.0 ]; 

        g_WebGLContext.bufferData( g_WebGLContext.ARRAY_BUFFER, new WebGLFloatArray(vertices), g_WebGLContext.STATIC_DRAW);

 

     }

 

     在这段程序里,主要的动作是:

 

     透过 getAttribLocation() 取得

抱歉!评论已关闭.