前一篇 《WebGL學習筆記 | 建立着色器程式》介紹了如何建立着色器程式,這次我們讓着色器程式運作起來,并在螢幕上繪制一個點。
1. 頂點着色器程式
完整的着色器程式分為頂點着色器程式和片元着色器程式,我們先看下頂點着色器的程式代碼,将它定義為一個JavaScript字元串:
- //頂點着色器程式
- var VSHADER_SOURCE = `
- void main() {
- //設定一個坐标點
- gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
- //設定點的大小
- gl_PointSize = 4.0;
- }
- `
頂點着色器是用來描述頂點特性比如:位置和大小,它是指二維或三維空間中的一個點,頂點着色器中有兩個内置變量:
- gl_Position:表示頂點位置
- gl_PointSize:表示點的尺寸(像素,預設為1.0)
上面代碼中 gl_Position 内置變量必須被指派,否則着色器就不能正常工作,gl_PointSize 則不是必須的,它的預設值為1.0 。
注意我們給 gl_Position 指派了一個矢量 vec4 它内部是由 4 個浮點數組成,但是這裡隻用了三個即:x、y、z,第四個分量設定為 1.0 在這裡被稱之為齊次坐标,因為它能夠提高處理三維資料的效率,是以被三維圖形系統大量使用。
當需要使用齊次坐标表示頂點坐标時,隻需要将最後一個分量置為 1.0 即可。
齊次坐标:齊次坐标使用(x, y, z, w)表示,等價于三維坐标(x/w, y/w, z/w),是以如果齊次坐标的第 4 個分量是 1,就可以将它當三維坐标使用。
2. 片元着色器程式
片元可以了解為逐像素處理過程,嚴格意義上說片元還包括:像素的位置、顔色和其它資訊。
- //片元着色器程式
- var FSHADER_SOURCE =`
- //設定點的顔色
- gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
- `;
gl_FragColor 是片元着色器中的唯一内置變量,它控制像素在螢幕上的最終顔色,上面代碼中的 vec4 的 4個分量代表顔色的 RGBA 值。
3. 在 JavaScript 啟用繪制
在 JavaScript 中初始化好着色器程式,進行編譯、連結,最後一步就是進行繪制操作:
//看上一篇《WebGL學習筆記 | 建立着色器程式》中有講解
- ...
- gl.useProgram(program);
- gl.drawArrays(gl.POINTS, 0, 1);
gl.drawArrays 函數非常強大,它可以用來繪制各種圖形,該函數規範如下:
gl.drawArrays(mode, first, count)
參數
mode:指定繪制方式,可接收以下常量:gl.POINTS、gl.LINES、gl.LINESTRIP、gl.LINELOOP、gl.TRIANGLES、gl.TRIANGLESTRIP、gl.TRIANGLEFAN
first:指定從那個頂點開始繪制(整型數)
count:指定繪制需要用到多少個頂點(整型數)
傳回值: 無
錯誤:
INVALID_ENUM 傳入的 mode 參數不是前述參數之一
INVALID_VALUE 參數 first 或 count 是負數
使用 gl.drawArrays() 時,頂點着色器将被執行 count 次,每次處理一個頂點,我們這裡隻繪制了一個點,是以count為1。
當頂點着色器執行完成後,片元着色器開始執行,将顔色值賦給 gl_FragColor,最後一個紅色的像素點被繪制到了螢幕的中心位置 (0.0, 0.0, 0.0) ,看下圖:
本篇教程完整源碼如下:
HelloPoint.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="utf-8" />
- <title>Draw a point (1)</title>
- </head>
- <body onload="main()">
- <canvas id="webgl" width="400" height="400">
- Please use a browser that supports "canvas"
- </canvas>
- <script src="HelloPoint1.js"></script>
- </body>
- </html>
HelloPoint.js
- //頂點着色器代碼
- gl_PointSize = 8.0;
- //片元着色器代碼
- //建立着色器程式并繪制
- function main() {
- // 擷取canvas标簽
- var canvas = document.getElementById('webgl');
- // 擷取webgl上下文對象
- var gl = canvas.getContext('webgl')//getWebGLContext(canvas);
- if (!gl) {
- console.log('Failed to get the rendering context for WebGL');
- return;
- //建立、編譯頂點Shader
- var vertexShader = gl.createShader(gl.VERTEX_SHADER);
- gl.shaderSource(vertexShader, VSHADER_SOURCE);
- gl.compileShader(vertexShader);
- if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
- var error = gl.getShaderInfoLog(vertexShader);
- console.log('Failed to compile shader: ' + error);
- gl.deleteShader(vertexShader);
- return null;
- //建立、編譯片元Shader
- var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
- gl.shaderSource(fragmentShader, FSHADER_SOURCE);
- gl.compileShader(fragmentShader);
- if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
- var error = gl.getShaderInfoLog(fragmentShader);
- gl.deleteShader(fragmentShader);
- //建立program對象,關聯、連結頂點、片元着色器
- var program = gl.createProgram();
- gl.attachShader(program, vertexShader);
- gl.attachShader(program, fragmentShader);
- gl.linkProgram(program);
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
- var error = gl.getProgramInfoLog(program);
- console.log('Failed to link program: ' + error);
- gl.deleteProgram(program);
- //啟用有着色器程式
- //通知webgl繪編