三角形
有人說三維模型的基本單元是三角形,比如復雜的游戲角色,也只是用許多三角形畫出來的,
不管上述說法是否屬實,本篇先把三角形畫出來,
如何繪制一個三角形
滑鼠點擊繪點示例我們寫了這樣的代碼:
points.forEach(item => {
gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
gl.drawArrays(gl.POINTS, 0, 1);
})

這種方法一次只能繪制一個點,
比如需要繪制一個三角形,應該是一個連貫的動作,比如在頂點著色器中一次性畫三個點,然后用線連接;而不是繪制一個點,在繪制一個點,在繪制一個點...,不應該是零散的
Tip:提前透露 - 只要一次性將三個點繪制出來,其實三角形也就畫出來了,
一次性繪制多個頂點可以使用緩沖區物件,
緩沖區物件
可以使用 webgl 中的緩沖區物件(buffer object) 一次性的向著色器傳入多個頂點資料,
使用緩沖區物件向頂點著色器傳入多個頂點資料,需要如下5個步驟:
創建緩沖區物件系結緩沖區物件寫入資料到緩沖區物件分配緩沖區物件給一個 attribute 變數開啟attribute 變數
一次繪制三個點
效果

實作
完整代碼如下:
// 一次繪制三個點
const VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
gl_PointSize = 10.0;
}
`
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 頂點資料
const vertices = {
// 頂點資料,Float32Array 當做普通陣列,對應 C 語言的 float
data: new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
]),
// 頂點數
vertexNumber: 3,
// 每個頂點分量數,例如第一個點(0.0, 0.5)
count: 2,
}
// 將頂點的位置寫入頂點著色器
initVertexBuffers(gl, vertices)
// gl.drawArrays(mode, first, count)
// 還是畫點(gl.POINTS),從緩沖區的第一個位置(0)開始畫,繪制3(vertices.vertexNumber)個點
gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
}
// 將三個頂點通過緩沖物件一次寫入著色器
function initVertexBuffers(gl, {data, count}) {
// 1. 創建緩沖區物件
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('創建緩沖區物件失敗');
return -1;
}
// 系結緩沖區物件系結到`目標`,只能通過目標向緩沖區寫資料
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 將資料寫入緩沖區
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
// 將整個緩沖區物件分配給 atttibute(a_Position) 變數
gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
// 激活 attribute 變數,使緩沖區對 attribute 變數分配生效
gl.enableVertexAttribArray(a_Position);
}
通過 initVertexBuffers() 將3個頂點分配給 attribute 變數,執行 gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber) 時,頂點著色器執行了3次,頂點著色器執行程序和緩沖區資料傳入程序如下:

繪制出所有點后,顏色緩沖區的內容就會自動顯示在瀏覽器中,
initVertexBuffers() 里面涉及了許多概念和api,請往下閱讀,
Float32Array
繪制三維圖形 webgl 需要處理大量相同的資料,例如頂點坐標,為了優化性能,就引入了一種特殊的陣列(型別化陣列),瀏覽器事先知道陣列中的資料型別,處理起來就更有效率,
Float32Array 就是其中一種型別化陣列,通常用于存盤頂點坐標或顏色,
Tip: webgl 中很多操作都需要用到型別化陣列,
webgl 中有如下型別化陣列:
| 陣列型別 | 每個元素所占位元組數 | 對應C語言的資料型別 |
|---|---|---|
| Int8Array | 1 | 8 位有符號整數 |
| Uint8Array | 1 | 8 位無符號整型 |
| Int16Array | 2 | 16 位有符號整數 |
| Uint16Array | 2 | 16 位無符號整型 |
| Int32Array | 4 | 32 位有符號的整型 |
| Uint32Array | 4 | 32 位無符號的整型 |
| Float32Array | 4 | 32 位的浮點數 float |
| Float64Array | 8 | 64 位的浮點數 double |
bindBuffer
gl.bindBuffer(target, buffer) - 系結緩沖區,例如示例中的: gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
為什么要將系結緩沖區物件?不能直接向緩沖區寫入資料,只能通過目標寫入資料,就像這樣:

目標表示緩沖區的用途,例如這里的 gl.ARRAY_BUFFER 指包含頂點屬性(例如頂點坐標、紋理坐標資料或頂點顏色資料)的緩沖區,
bufferData
gl.bufferData(target, size, usage) - 將資料寫入緩沖區,例如示例中的:gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.STATIC_DRAW 是 usage 中的一種,指緩沖區的內容可能經常使用,而不會經常更改,內容被寫入緩沖區,但不被讀取,
此方法執行后,webgl 系統內部狀態如下:

vertexAttribPointer
g.vertexAttribPointer(index, size, type, normalized, stride, offset) - 將整個緩沖區物件分配給 atttibute 變數,
例如示例中的:
// count - 指定緩沖區每個頂點的分量個數,缺少則按照 vertexAttrib3f 的補全方式
// gl.FLOAT 與上文的 Float32Array 對應
// normalized - 對于型別gl.FLOAT和gl.HALF_FLOAT,此引數無效
// stride - 指定相鄰兩個頂點間的位元組數,默認為0
// offset - 指定緩沖區物件中的偏移量,即attribute 從緩沖區何處開始存盤
gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
此方法執行后,webgl 系統內部狀態如下:

疑惑: 說vertexAttribPointer最后一個引數(offset)必須是型別的位元組長度的倍數,嘗試將 0 改成 4,效果卻是:

enableVertexAttribArray
gl.enableVertexAttribArray(index) - 激活 attribute 變數,使緩沖區對 attribute 變數分配生效,
此方法執行后,webgl 系統內部狀態如下:

drawArrays
gl.drawArrays(mode, first, count) - 執行頂點著色器,按照 mode 指定的引數繪制圖形,first 指定從哪個點開始繪制,count 指繪制需要幾個點,
能繪制的圖形有:

Tip: drawArrays 更多細節請看這里
三角形
只要一次性將三個點繪制出來,其實三角形也就畫出來了,
修改上面示例一行代碼就好(gl.POINTS -> gl.LINE_LOOP):
// 前
gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
// 后
gl.drawArrays(gl.LINE_LOOP, 0, vertices.vertexNumber);
效果如下:

矩形
效果如下:

核心代碼如下:
// 定義四個點
const vertices = {
data: new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5,
0.5, -0.5,
]),
// 頂點數
vertexNumber: 4,
count: 2,
}
initVertexBuffers(gl, vertices)
// TRIANGLE_STRIP
gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.vertexNumber);
Tip:四個點的順序有要求
三角扇
效果如下:

在矩形的基礎上,改為 gl.TRIANGLE_FAN 即可,
Tip:GL_TRIANGLE_FAN - 繪制各三角形形成一個扇形序列,以 v0 為起點:(v0, v1, v2)、(v0, v2, v3)、(v0, v3, v4)
出處:https://www.cnblogs.com/pengjiali/p/17183771.html
本文著作權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/545974.html
標籤:其他
上一篇:前端設計模式——工廠模式
