這是面向前端的圖形學課程。但願用淺顯的語言爲你們解釋清圖形學的一些概念。咱們使用的前端最容易webgl,雖然難免仍是要接觸GLSL,別打我,這已是最簡單的!
咱們開始第一節,矩陣....不仍是畫個三角形吧!javascript
說人話webgl就是個工具,能夠拿來畫圖的,它依賴於canvas,在canvas上你能夠獲取2d的上下文,也能夠獲取webgl的上下文。相似寶可夢新手村能夠選蒜頭王八也能夠選黃皮耗子html
因此第一步咱們就建立一個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.clearColor
與gl.clear
java
gl.clearColor
接受(r,g,b,a)
的顏色數據,至關於給畫布選一個底色;gl.clear
用來清除buffer,至於什麼是buffer咱們後面會講。這裏只須要知道gl在沒次繪圖時均可以記錄顏色和深度兩個buffer,再次繪製時須要清除!web
着色器是足以熄滅你學習webgl的熱情的魔鬼。但理解以後你也會以爲豁然開朗!首先三維空間的物體無論多複雜都是一些集合體,所謂點動成線,線動成面,面動成體。
因此點是描述空間物體的最小單元。編程
頂點着色器就是用來處理咱們傳入的頂點的,在下節課咱們將使用頂點着色器對三角形進行旋轉,縮放,平移等操做。
而與之相對的是片斷着色器,它主要處理圍成圖形的上色工做。
這並非明確的定義,但有助於你的理解:頂點着色器提供裁剪空間座標值
而片段着色器提供顏色值
canvas
着色器的語言與js不一樣是一種類c語言,就是大學計算機學的那種須要main函數的語言。因此做色器的語法總結起來就是api
// 各類變量 balabala void main() { //各類操做 balabala }
上文咱們說過能夠把變量傳入着色器,因爲做色器如今咱們來講下常見的變量數組
下面咱們來寫一個最簡單的頂點做色器函數
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>
這裏咱們定義了兩個屬性vertPosition
與vertColor
用來存儲傳入的位置與顏色信息,可變量 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真乃好發明!
寫完上面的代碼,其實着色器仍是個半成品!
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);
這樣全部的前置工做都已經作好了,下面能夠開始畫三角形了。心累
前面咱們說過在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);
大功告成
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, ];
若是想要繪製正方形,只要多加一組點便可。把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,下期咱們將開啓一個比較硬核的話題矩陣