1. 程式人生 > >WebGL學習系列-第一個程式

WebGL學習系列-第一個程式

前言

本篇學習第一個WebGL程式——畫一個點,通過此程式來理解WebGL程式的結構,這是所有後續知識的開端。

畫一個點

先看一下效果圖:
畫點效果圖

為了畫這麼一個點,在WebGL可不太簡單,它會涉及到WebGL上下文以及著色器的概念,不要著急,咱們慢慢來理解。

WebGL上下文

學過Canvas的同學應該都知道,想要在瀏覽器中使用Canvas畫圖,需要先取得一個上下文,就像建立一個場景一樣,有了場景才可以繪製。在WebGL中,想要進行繪製,一樣需要先獲取一個上下文,而且跟Canvas 2D開發上下文的獲取非常的類似。

1、首先,要有一個canvas dom元素

<canvas
id="webgl" width="400" height="400">
您的瀏覽器不支援canvas,建議使用Chrome瀏覽器 </canvas>

2、獲取上一步得到的 canvas dom元素,然後獲取WebGL上下文

var canvas = document.getElementById('webgl');
// 獲取WebGL上下文
function createWebGLContext(canvas){
    var names = ["experimental-webgl", "webgl" , "webkit-3d", "moz-webgl"];
    var
webglContext = null; for (var i = 0; i < names.length; i++) { try { webglContext = canvas.getContext(names[i]); if(webglContext){ break; } } catch(e) {} } return webglContext; }

考慮到相容性,所以我們嘗試多種獲取WebGL上下文的方式,只要任何一種獲取成功即可返回WebGL上下文。

背景清除

學習WebGL開發的時候,有個非常重要的概念就是清除重繪。打個比方,我們想要在介面上畫兩個矩形,先畫第一個,然後隔5秒後再畫第二個,最終介面上顯示2個矩形。那麼正確的做法是先繪製第一個矩形,然後使用一個5秒的定時器,5秒後清空之前繪製的所有內容,然後繪製兩個矩形。說白了,就是你所看到的東西是一次性繪製出來的,而不能先繪製一點,隔一會再繪製一點,每一次繪製都要進行整個畫面的重繪。而在重繪的時候,一般我們都會設定一個重繪的預設背景顏色,設定一次即可。

// 這裡指定預設背景色為黑色,不透明
context.clearColor(0.0, 0.0, 0.0, 1.0);

設定完預設背景色之後,在每一次繪製之前,我們手動清除繪製區域,然後重繪整個畫面。

// 清空
context.clear(context.COLOR_BUFFER_BIT);

注意到這裡我們使用 context.COLOR_BUFFER_BIT,其實,我們每一次繪製的時候,可能要畫非常多的圖形,不是說我們畫一個圖形就立馬在瀏覽器上顯示的,那樣很可能出現閃屏,所以我們繪製其實是繪製在稱為顏色緩衝區的一塊記憶體空間來的,等全部繪製好之後,再一次性重新整理到瀏覽器展現,這樣才不會閃屏。除了COLOR_BUFFER_BIT(顏色緩衝區),以後我們還會接觸context.DEPTH_BUFFER_BIT(深度緩衝區),後續篇章再解釋。

繪製

有了上下文,知道了繪製的方法,接下來我們就要繪製了,本篇我們要繪製一個點,為了繪製一個點,我們要有繪製點的api,要知道點的位置,大小和顏色。

我們使用 context.drawArrays(context.POINTS, 0, 1); 來繪製一個點,drawArrays可以用於繪製點、線段、扇形等各類形狀,引數簡要介紹如下:

繪製基本圖形:
drawArrays(mode , first  , count)   

mode:  繪製的形狀,用於決定點是怎麼連線成圖形的
first: 頂點開始索引
count: 使用多少個頂點進行繪製

此api能夠繪製的基本圖形如下所示:

基本圖形

有了繪製的api還不夠,我們發現沒有地方指定點的位置、大小、顏色之類的屬性,這對於新學習的朋友一定感到很迷惑,到現在還沒有看到可以設定點屬性的地方,這也是WebGL比較麻煩,但也是非常精華的一部分,這一塊實際上是由著色器來完成的。

先來看一張圖:

著色器示意圖

在WebGL中,有兩類著色器,分別為頂點著色器和片元著色器,它們的作用上圖已經表述得很清楚了,著色器其實就是一段程式碼,然後會被底層不斷的呼叫,從而獲取繪製所需要的點的一些資訊。為了使用著色器,需要建立一個program物件,program物件內部再繫結頂點著色器和片元著色器物件,而著色器物件又是需要我們建立的,建立後指定程式碼片段字串即可,最後在context中,使用這個program即可,drawArrays介面內部會自行跟program中的著色器通訊對接。

再來看一張頂點著色器的示意圖:

頂點著色器示意圖

再對應到程式碼中就很容易理解了,片元著色器的使用流程也是一樣的。

本示例中我們的頂點著色器程式碼片段如下:

// 頂點著色器程式碼(決定頂點在哪裡、大小、顏色)
var VSHADER_SOURCE = 
  'void main() {\n' +
  '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 設定頂點的位置
  '  gl_PointSize = 10.0;\n' +                    // 設定頂點的大小
  '}\n';

注意,著色器的程式碼片段是以字串形式存在 的,而且像其他語言一樣有個main函式,頂點著色器有幾個內建變數,目前我們使用了gl_Position 和 gl_PointSize ,分別表示頂點的位置和大小,而且我們在這裡固化了變數的值,以後再來研究怎麼實現動態變數,現在你要知道,當我們執行drawArrays的時候,底層會執行頂點著色器的程式碼,獲取點的位置和大小。

片元著色器的程式碼如下:

var FSHADER_SOURCE =
      'void main() {\n' +
      '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 設定頂點的顏色
      '}\n';`````````

在片元著色器中,我們使用了內部屬性 gl_FragColor,用於表示畫素的顏色值,此程式碼片段在真正執行時,對於每一個畫素,都會被呼叫一次,所以稱之為片元著色器也挺符合的。

小結

經過上面的分析,我們發現在WebGL中繪製一個點確實不太容易,但是當理清了各個涉及到的知識點之後,畫其他東西原理都差不多的,最主要還是要理解著色器的概念,這只是第一篇,往後隨著不斷學習,不斷加深對著色器的理解。

原始碼

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>第一個WebGL程式-畫一個點</title>
  </head>
  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
        您的瀏覽器不支援canvas,建議使用Chrome瀏覽器
    </canvas>
    <script>    
    // 主程式入口
    function main(){
        var canvas = document.getElementById('webgl');
        var context = createWebGLContext(canvas);
        var program = createProgram(context , VSHADER_SOURCE ,FSHADER_SOURCE);
        context.program = program;
        context.useProgram(program);

        // 每一次重繪時的背景色
        context.clearColor(0.0, 0.0, 0.0, 1.0);
        // 清除 <canvas>
        context.clear(context.COLOR_BUFFER_BIT);
        // 畫一個點
        context.drawArrays(context.POINTS, 0, 1);
    }

    // 獲取WebGL上下文
    function createWebGLContext(canvas){
        var names = ["experimental-webgl", "webgl" , "webkit-3d", "mozwebgl"];
        var webglContext = null;
        for (var i = 0; i < names.length; i++) {
            try {
                webglContext = canvas.getContext(names[i]);
                if(webglContext){
                    break;
                }
            } catch(e) {}

        }
        return webglContext;
    }
    // 建立一個program(相當於著色器的上下文)
    function createProgram(context, vshader, fshader){
        var vertexShader = loadShader(context, context.VERTEX_SHADER,vshader);
        var fragmentShader = loadShader(context,context.FRAGMENT_SHADER,fshader);
        var program = context.createProgram();
        context.attachShader(program, vertexShader);
        context.attachShader(program, fragmentShader);

        context.linkProgram(program);
        return program;
    }   
    function loadShader(context, type, source){
        var shader = context.createShader(type);
        context.shaderSource(shader, source);
        context.compileShader(shader);
        return shader;
    }

    // 頂點著色器程式碼(決定頂在哪裡,大小)
    var VSHADER_SOURCE = 
      'void main() {\n' +
      '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 設定頂點的位置
      '  gl_PointSize = 10.0;\n' +                    // 設定頂點的大小
      '}\n';

    // 片元著色器程式碼(給畫素上色)
    var FSHADER_SOURCE =
      'void main() {\n' +
      '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 設定頂點的顏色
      '}\n';
    </script>
  </body>
</html>

原始碼下載