《前端圖形學從入門到放棄》001 畫一個三角形

這是面向前端的圖形學課程。但願用淺顯的語言爲你們解釋清圖形學的一些概念。咱們使用的前端最容易webgl,雖然難免仍是要接觸GLSL,別打我,這已是最簡單的!

咱們開始第一節,矩陣....不仍是畫個三角形吧!javascript

webgl是什麼?

說人話webgl就是個工具,能夠拿來畫圖的,它依賴於canvas,在canvas上你能夠獲取2d的上下文,也能夠獲取webgl的上下文。相似寶可夢新手村能夠選蒜頭王八也能夠選黃皮耗子html

image

因此第一步咱們就建立一個canvas,並獲取webgl上下文前端

<canvas id="canvas" width="1000" height="1000"></canvas>
<script>
var canvas = document.querySelector("#canvas");
var gl = canvas.getContext("webgl");
</script>

開始擦滑板咯!小畫家

這樣咱們就獲取了canvas的掌控權,canvas至關於一塊畫布,在畫圖以前咱們得像保證畫面是乾淨的。
gl提供了兩個api來作這件事:gl.clearColorgl.clearjava

gl.clearColor接受(r,g,b,a)的顏色數據,至關於給畫布選一個底色;
gl.clear用來清除buffer,至於什麼是buffer咱們後面會講。這裏只須要知道gl在沒次繪圖時均可以記錄顏色和深度兩個buffer,再次繪製時須要清除!web

頂點着色器和片斷着色器

着色器是足以熄滅你學習webgl的熱情的魔鬼。但理解以後你也會以爲豁然開朗!首先三維空間的物體無論多複雜都是一些集合體,所謂點動成線,線動成面,面動成體。
因此點是描述空間物體的最小單元。
image編程

頂點着色器就是用來處理咱們傳入的頂點的,在下節課咱們將使用頂點着色器對三角形進行旋轉,縮放,平移等操做。
而與之相對的是片斷着色器,它主要處理圍成圖形的上色工做。
這並非明確的定義,但有助於你的理解:頂點着色器提供裁剪空間座標值片段着色器提供顏色值canvas

着色器的語言與js不一樣是一種類c語言,就是大學計算機學的那種須要main函數的語言。因此做色器的語法總結起來就是api

// 各類變量 balabala
void main() {
    //各類操做 balabala
}

上文咱們說過能夠把變量傳入着色器,因爲做色器如今咱們來講下常見的變量數組

  • Attributes(屬性):屬性用來指明怎麼從緩衝中獲取所需數據並將它提供給頂點着色器。 例如你可能在緩衝中用三個32位的浮點型數據存儲一個位置值。
  • buffer(緩衝):緩衝是發送到GPU的一些二進制數據序列,一般狀況下緩衝數據包括位置,法向量,紋理座標,頂點顏色值等。 你能夠存儲任何數據。
  • Uniforms(全局變量):全局變量在着色程序運行前賦值,在運行過程當中全局有效。
  • Textures(紋理):紋理是一個數據序列,能夠在着色程序運行中隨意讀取其中的數據。大多數狀況咱們不會本身寫材質,就直接用紋理了。
  • Varyings(可變量):可變量是一種頂點着色器給片段着色器傳值的方式,依照渲染的圖元是點, 線仍是三角形,頂點着色器中設置的可變量會在片段着色器運行中獲取不一樣的插值。

下面咱們來寫一個最簡單的頂點做色器函數

gl接受一個字符串的做色器代碼,你可使用數組或任意形式提供這個字符串,我我的一般寫在一個script標籤中:

<script id="vertex-shader-2d" type="notjs">
 // 一個屬性變量,將會從緩衝中獲取數據
    attribute vec2 vertPosition;
    attribute vec3 vertColor;
    varying vec3 fragColor;
    // 全部着色器都有一個main方法
    void main() {
      fragColor = vertColor;
      // gl_Position 是一個頂點着色器主要設置的變量
      gl_Position = vec4(vertPosition,0.0,1.0);
    }
</script>

這裏咱們定義了兩個屬性vertPositionvertColor用來存儲傳入的位置與顏色信息,可變量 fragColor會繼續傳遞給片斷着色器使用。
內置變量gl_Position的值是四維向量vec4(x,y,z,1.0),前三個參數表示頂點的xyz座標值,第四個參數是浮點數1.0
因爲案例中咱們只須要繪製一個平面內的三角形,因此z值被咱們設爲0。
與之相對咱們也能寫出片斷着色器:

<script id="fragment-shader-2d" type="notjs">
    // 片段着色器沒有默認精度,因此咱們須要設置一個精度
    // mediump是一個不錯的默認值,表明「medium precision」(中等精度)
    precision mediump float;
    varying vec3 fragColor;
    void main() {
      // gl_FragColor是一個片段着色器主要設置的變量
      gl_FragColor = vec4(fragColor, 1.0); // 
    }
</script>

使用做色器

webgl讓初學者窩火的另外一個緣由是,他的編程邏輯很像底層語言,而非javascript,沒有不少封裝。作一件小事每每要執行多個方法。例如咱們下面要使用做色器,寫的這段語法:

// 咱們先建立兩個着色器
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 這兩個詳單于做色器的容器,但還未被填充內容。因此想在須要獲取咱們前面寫好的頂點做色器和片斷做色器
var vertexShaderText = document.querySelector("#vertex-shader-2d").text;
var fragmentShaderText = document.querySelector("#fragment-shader-2d").text;
// 以後,咱們要把二者關聯起來
gl.shaderSource(vertexShader, vertexShaderText);
gl.shaderSource(fragmentShader, fragmentShaderText);
// 最後再編譯着色器,才能供咱們使用!
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);

心裏戲:three.js真乃好發明!

這麼還不能用,program上場!

寫完上面的代碼,其實着色器仍是個半成品!

WebGL在電腦的GPU中運行。所以你須要使用可以在GPU上運行的代碼。這樣的代碼須要提供成對的方法。每對方法中一個叫頂點着色器,另外一個叫片段着色器,這些事你已經知道了!
而把他兩組合起來就是program!

var program = gl.createProgram();
// 咱們須要先建立program
// 再把以前申明好的做色器附加到program上
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 而後咱們將這兩個着色器 _link_(連接)到一個 _program_(着色程序)
gl.linkProgram(program);
// 檢查 WebGLProgram 程序是否連接成功的同時還會檢查其是否能在當前的 WebGL 中使用。
gl.validateProgram(program);

這樣全部的前置工做都已經作好了,下面能夠開始畫三角形了。心累

建立buffer

前面咱們說過在webgl中頂點和顏色信息被存在buffer上。
這裏咱們就偷懶把他們寫在一個數組裏:

var triangleVertices = 
      [ // X, Y,       R, G, B
        -0.5, -0.5,    1.0, 0.0, 0.0,
        -0.5, 0.5,     1.0, 1.0, 0.0,
        0.5, -0.5,     1.0, 1.0, 0.0,
      ];

上文咱們知道着色器須要調用createShader方法建立,buffer天然如法炮製:

var triangleVertexBufferObject = gl.createBuffer();
// 綁定buffer數據類型
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexBufferObject);
// 關聯變量與數據
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);
// 雖然咱們擁有了頂點和顏色數據的數組,但沒法直接用。
// WebGL須要強類型數據,因此使用new Float32Array(triangleVertices)建立了32位浮點型數據序列
// 最後一個參數gl.STATIC_DRAW是提示WebGL咱們將怎麼使用這些數據。

仍是沒用!

但此時buffer上的數據和着色器上的數據還未對應,所以須要吧着色器的屬性取出。

var positionAttribLocation = gl.getAttribLocation(program, 'vertPosition');
var colorAttribLocation = gl.getAttribLocation(program, 'vertColor');
// 並從triangleVertices中複製數據到序列中,而後gl.bufferData複製這些數據到GPU的Buffer對象上。
// 因爲咱們的頂點數據和顏色數據寫在了一塊兒,咱們須要告訴webgl哪些是頂點哪些是顏色
gl.vertexAttribPointer(
        positionAttribLocation, // 上文獲取的本地屬性
        2, // 幾個值能表示這個屬性,這裏是座標只須要2個值x,y
        gl.FLOAT, // 類型
        gl.FALSE,
        5 * Float32Array.BYTES_PER_ELEMENT, // 步幅
        0 // 起始點
      );
      gl.vertexAttribPointer(
        colorAttribLocation, 
        3, // 這裏是顏色須要3個值r,g,b
        gl.FLOAT, 
        gl.FALSE,
        5 * Float32Array.BYTES_PER_ELEMENT, 
        2 * Float32Array.BYTES_PER_ELEMENT 
      );
// 啓動屬性

gl.enableVertexAttribArray(positionAttribLocation);
gl.enableVertexAttribArray(colorAttribLocation);
gl.useProgram(program);
// gl.useProgram就與以前講到的gl.bindBuffer類似,設置當前使用的着色程序。

繪製

至此咱們就大功告成了,最後一步就是繪製,因爲咱們使用的是三角形面,一個三角形僅僅須要3個頂點,因此最終執行

gl.drawArrays(gl.TRIANGLES, 0, 3);

大功告成
image

var triangleVertices = 
     [ // X, Y,       R, G, B
       -0.5, -0.5,    1.0, 0.0, 0.0,
       -0.5, 0.5,  1.0, .0, 0.0,
       0.5, -0.5,   1.0, .0, 0.0,
       
     ];

image

若是想要繪製正方形,只要多加一組點便可。把triangleVertices改成boxVertices

var boxVertices = 
      [ // X, Y,       R, G, B
        -0.5, -0.5,    1.0, 0.0, 0.0,
        -0.5, 0.5,  1.0, 1.0, 0.0,
        0.5, -0.5,   1.0, 1.0, 0.0,
        -0.5, 0.5,    1.0, 1.0, 0.0,
        0.5, 0.5,  0.0, 1.0, 0.0,
        0.5, -0.5,   1.0, 1.0, 0.0
      ];

但此時繪製的點就再也不是3個,而是6個,所以修改繪製方法爲:
gl.drawArrays(gl.TRIANGLES, 0, 6);

下期預告

這期做爲一個熱身,熟悉一下webgl的api,下期咱們將開啓一個比較硬核的話題矩陣

相關文章
相關標籤/搜索