- 原文地址:Day 5. Interleaved buffers
- 原文做者:Andrei Lesnitsky
這是 WebGL 系列的第 5 天教程,天天都有新文章發佈。html
源代碼在這裏github
第 4 天咱們已經學會了如何使用着色器的 varyings
。今天咱們將探討另外一個概念,咱們先把昨天的做業解決:web
WebGL 是與 GPU 協同渲染內容的 API。JavaScript 是由 v8 在 CPU 上執行的,雖然 GPU 沒法執行 JavaScript,但仍能夠對其編程。編程
GPU 可以識別 GLSL 語言,咱們不只會熟悉 WebGL API,還會熟悉這種新語言。canvas
GLSL 是一種相似於 C 的編程語言,所以對於 JavaScript 開發人員來講很容易學習和編寫。編程語言
可是,咱們在哪裏編寫 glsl 代碼?如何將其傳遞給 GPU 以執行?函數
接下來,咱們建立一個新的 js 文件,並獲取對 WebGL 渲染上下文的引用學習
📄 index.htmlwebgl
</head> <body> <canvas></canvas> - <script src="./src/canvas2d.js"></script> + <script src="./src/webgl-hello-world.js"></script> </body> </html>
📄 src/webgl-hello-world.js
const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl');
GPU 可執行的程序是經過 WebGL 渲染上下文方法建立的。
📄 src/webgl-hello-world.js
const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl'); + + const program = gl.createProgram();
GPU程序包含兩個「功能」,
這些功能稱爲 shaders
WebGL,支持多種類型的着色器
在這個示例中,咱們將使用 vertex
和 fragment
着色器。
二者均可以使用 createShader
方法建立
📄 src/webgl-hello-world.js
const gl = canvas.getContext('webgl'); const program = gl.createProgram(); + + const vertexShader = gl.createShader(gl.VERTEX_SHADER); + const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
如今讓咱們編寫最簡單的着色器:
📄 src/webgl-hello-world.js
const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + + const vShaderSource = ` + void main() { + + } + `;
對於具備必定 C/C++ 經驗的人來講,這應該看起來很熟悉。
不像 C 或 C++ 語言的 main
沒有返回值,這裏的 main
會分配一個值到全局變量 gl_Position
。
📄 src/webgl-hello-world.js
const vShaderSource = ` void main() { - + gl_Position = vec4(0, 0, 0, 1); } `;
如今,讓咱們仔細看一下分配的內容。
着色器中有不少功能。
vec4
函數建立一個由 4 個份量組成的向量。
gl_Position = vec4(0, 0, 0, 1);
看起來很奇怪,咱們生活在三維世界中,第四部分究竟是什麼?是時間嗎?😕
並非的
事實證實,這種添加容許使用許多不錯的技術來處理 3D 數據。
在經典的笛卡爾座標系中定義了三維座標點,附加的第四維將此點更改成齊次座標。它仍然表明三維空間中的一個點,而且能夠經過一對簡單的函數,輕鬆演示如何構造此類座標。
如今,咱們能夠忽略第四部分組件,並將其設置爲 1.0
好的,咱們有一個着色器變量,另外一個變量中有着色器源。咱們如何鏈接這兩個呢?
📄 src/webgl-hello-world.js
gl_Position = vec4(0, 0, 0, 1); } `; + + gl.shaderSource(vertexShader, vShaderSource);
GLSL 着色器應進行編譯才能執行:
📄 src/webgl-hello-world.js
`; gl.shaderSource(vertexShader, vShaderSource); + gl.compileShader(vertexShader);
能夠用檢索找到編譯結果,此方法返回 compiler
並輸出。若是是空字符串,仍然都很好。
📄 src/webgl-hello-world.js
gl.shaderSource(vertexShader, vShaderSource); gl.compileShader(vertexShader); + + console.log(gl.getShaderInfoLog(vertexShader));
咱們須要對片斷着色器執行相同的操做,同時咱們實現一個輔助功能,該功能也將用於片斷着色器。
📄 src/webgl-hello-world.js
} `; - gl.shaderSource(vertexShader, vShaderSource); - gl.compileShader(vertexShader); + function compileShader(shader, source) { + gl.shaderSource(shader, source); + gl.compileShader(shader); - console.log(gl.getShaderInfoLog(vertexShader)); + const log = gl.getShaderInfoLog(shader); + + if (log) { + throw new Error(log); + } + } + + compileShader(vertexShader, vShaderSource);
最簡單的片斷着色器的外觀如何?徹底相同。
📄 src/webgl-hello-world.js
} `; + const fShaderSource = ` + void main() { + + } + `; + function compileShader(shader, source) { gl.shaderSource(shader, source); gl.compileShader(shader);
片斷着色器的計算結果是一種顏色,它也是 4 個份量 (r, g, b, a)
的向量。與 CSS 不一樣,值在[0..1]
範圍內,而不是[0..255]
。片斷着色器的計算結果應分配給變量gl_FragColor
。
📄 src/webgl-hello-world.js
const fShaderSource = ` void main() { - + gl_FragColor = vec4(1, 0, 0, 1); } `; } compileShader(vertexShader, vShaderSource); + compileShader(fragmentShader, fShaderSource);
如今咱們應該使用着色器鏈接 program
。
📄 src/webgl-hello-world.js
compileShader(vertexShader, vShaderSource); compileShader(fragmentShader, fShaderSource); + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader);
接下來是連接程序。須要在這一階段來驗證頂點着色器和片斷着色器是否相互兼容(咱們將在後面詳細介紹)。
📄 src/webgl-hello-world.js
gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); + + gl.linkProgram(program);
咱們的應用程序可能有幾個程序,因此咱們應該在發出繪圖調用以前告訴 gpu 咱們要使用哪一個程序。
📄 src/webgl-hello-world.js
gl.attachShader(program, fragmentShader); gl.linkProgram(program); + + gl.useProgram(program);
好了,咱們準備畫點東西。
📄 src/webgl-hello-world.js
gl.linkProgram(program); gl.useProgram(program); + + gl.drawArrays();
WebGL 能夠渲染幾種類型的"原語"
接着咱們傳遞要渲染的原始類型
📄 src/webgl-hello-world.js
gl.useProgram(program); - gl.drawArrays(); + gl.drawArrays(gl.POINTS);
有一種方法能夠將包含有關圖元位置信息的輸入數據傳遞到頂點着色器,所以咱們須要將要渲染的第一個原語的索引進行傳遞。
📄 src/webgl-hello-world.js
gl.useProgram(program); - gl.drawArrays(gl.POINTS); + gl.drawArrays(gl.POINTS, 0);
還有原語的計數
📄 src/webgl-hello-world.js
gl.useProgram(program); - gl.drawArrays(gl.POINTS, 0); + gl.drawArrays(gl.POINTS, 0, 1);
什麼都沒呈現😢出現什麼問題了?