WebGL之旅(三)VBO和多種圖形繪製

通過gl.vertexAttribXX和gl.uniformXX函數,一次只能向shader中傳遞一個變量,要一次性向shader中傳遞多個數據就要用到頂點緩衝區對象(VBO)。

一 VBO

VBO使用步驟:

  1. 創建緩衝區對象(gl.createBuffer());
  2. 綁定緩衝區對象(gl.bindBuffer());
  3. 將數據寫入緩衝區(gl.bufferData());
  4. 將緩衝區對象分配給一個attribute變量(gl.vetextAttribPointer());
  5. 開啓attribute變量(gl。enableVetextAttribArray())。

示例:

/**
 * VBO
 * [email protected]
 * */

// 頂點着色器源碼
var vertexShaderSrc = `
attribute vec4 a_Position;// 接收傳入位置座標,必須聲明爲全局
attribute float a_PointSize;// 接收傳入位置座標,必須聲明爲全局
void main(){
    gl_Position = a_Position;// gl_Position 內置變量,表示點的位置,必須賦值
    gl_PointSize = a_PointSize;// gl_PointSize 內置變量,表示點的大小(單位像素),可以不賦值,默認爲1.0
}`;

// 片段着色器源碼
var fragmentShaderSrc = `
void main(){
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);// gl_FragColor 內置變量,表示片元顏色,必須賦值
}`;

// 初始化使用的shader
function initShader(gl) {
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);// 創建頂點着色器
    gl.shaderSource(vertexShader, vertexShaderSrc);// 綁定頂點着色器源碼
    gl.compileShader(vertexShader);// 編譯定點着色器

    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);// 創建片段着色器
    gl.shaderSource(fragmentShader, fragmentShaderSrc);// 綁定片段着色器源碼
    gl.compileShader(fragmentShader);// 編譯片段着色器

    var shaderProgram = gl.createProgram();// 創建着色器程序
    gl.attachShader(shaderProgram, vertexShader);// 指定頂點着色器
    gl.attachShader(shaderProgram, fragmentShader);// 指定片段着色色器
    gl.linkProgram(shaderProgram);// 鏈接程序
    gl.useProgram(shaderProgram);//使用着色器

    gl.program = shaderProgram;
}

function main() {
    var canvas = document.getElementById("container");
    var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    initShader(gl);// 初始化着色器
    var n = initVertexBuffers(gl);// 初始化頂點

    var a_PointSize = gl.getAttribLocation(gl.program, "a_PointSize");// 獲取shader中的a_PointSize變量
    gl.vertexAttrib1f(a_PointSize, 10.0);// 給a_PointSize賦值

    gl.clearColor(0.0, 0.0, 0.0, 1.0);// 指定清空canvas的顏色
    gl.clear(gl.COLOR_BUFFER_BIT);// 清空canvas
    gl.drawArrays(gl.POINTS, 0, n);
}

function initVertexBuffers(gl) {
    var vertices = new Float32Array([
        0.0, 0.5,
        -0.5, -0.5,
        0.5, -0.5
    ]);

    var vertexBuffer = gl.createBuffer();// 創建緩衝區對象
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 將緩衝區對象綁定到目標
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 向緩衝區中寫入數據

    var a_Position = gl.getAttribLocation(gl.program, "a_Position");// 獲取a_Position變量
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);// 將緩衝區對象分配給a_Position
    gl.enableVertexAttribArray(a_Position);// 鏈接a_Position與分配給他的緩衝區對象

    return vertices.length / 2;
}

如圖:

這裏寫圖片描述

通過initVertexBuffers函數,一次性向緩衝區寫入了三個頂點,繪製每個頂點時,都會執行一次頂點着色器程序。

另外,需要注意的時,通過vertexAttribPointer函數,我們只給a_Position傳遞了兩個值,另外兩個值將被自動填充爲0.0和1.0f。

多種圖形的繪圖

前面我們通過gl.drawArrays(gl.POINTS, 0, 3);繪製了三個點,將分別修改爲:

  1. gl.drawArrays(gl.POINTS, 0, 2); 繪製2個點
  2. gl.drawArrays(gl.POINTS, 0, 1); 繪製1個點

可以看到,可以通過修改第三個參數來控制繪製點的個數。

修改第一個參數呢?將代碼做個小得調整:

  1. 刪掉gl_PointSize相關代碼,gl_PointSize只在繪製點時生效
  2. gl.POINTS修改爲gl.TRANGLES

如下:

/**
 * 繪製多種圖形
 * [email protected]
 * */

// 頂點着色器源碼
var vertexShaderSrc = `
attribute vec4 a_Position;// 接收傳入位置座標,必須聲明爲全局
void main(){
    gl_Position = a_Position;// gl_Position 內置變量,表示點的位置,必須賦值
}`;

// 片段着色器源碼
var fragmentShaderSrc = `
void main(){
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);// gl_FragColor 內置變量,表示片元顏色,必須賦值
}`;

// 初始化使用的shader
function initShader(gl) {
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);// 創建頂點着色器
    gl.shaderSource(vertexShader, vertexShaderSrc);// 綁定頂點着色器源碼
    gl.compileShader(vertexShader);// 編譯定點着色器

    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);// 創建片段着色器
    gl.shaderSource(fragmentShader, fragmentShaderSrc);// 綁定片段着色器源碼
    gl.compileShader(fragmentShader);// 編譯片段着色器

    var shaderProgram = gl.createProgram();// 創建着色器程序
    gl.attachShader(shaderProgram, vertexShader);// 指定頂點着色器
    gl.attachShader(shaderProgram, fragmentShader);// 指定片段着色色器
    gl.linkProgram(shaderProgram);// 鏈接程序
    gl.useProgram(shaderProgram);//使用着色器

    gl.program = shaderProgram;
}

function main() {
    var canvas = document.getElementById("container");
    var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    initShader(gl);// 初始化着色器
    var n = initVertexBuffers(gl);// 初始化頂點

    gl.clearColor(0.0, 0.0, 0.0, 1.0);// 指定清空canvas的顏色
    gl.clear(gl.COLOR_BUFFER_BIT);// 清空canvas
    gl.drawArrays(gl.TRIANGLES, 0, 3);
}

function initVertexBuffers(gl) {
    var vertices = new Float32Array([
        0.0, 0.5,
        -0.5, -0.5,
        0.5, -0.5
    ]);

    var vertexBuffer = gl.createBuffer();// 創建緩衝區對象
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 將緩衝區對象綁定到目標
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 向緩衝區中寫入數據

    var a_Position = gl.getAttribLocation(gl.program, "a_Position");// 獲取a_Position變量
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);// 將緩衝區對象分配給a_Position
    gl.enableVertexAttribArray(a_Position);// 鏈接a_Position與分配給他的緩衝區對象

    return vertices.length / 2;
}

如圖:

這裏寫圖片描述

參數說明:

  • POINTS:繪製一個個點單;
  • LINES:在V0-V1,V2-V3,…之間劃線,奇數個點最後一個會被忽略;
  • LINE_STRIP: 依次收尾鏈接v0-v1-v2-v3…;
  • LINE_LOOP:在所有點之間兩兩連線;
  • TRIANGLES: 繪製三角形v0-v1-v2, v3-v4-v5,…;
  • TRIANGLE_STRIP:任意連續三個點之間繪製三角形;
  • TRIANGLE_FAN:複用上一個三角形的一邊與下一個點,繪製一個三角形。

爲了測試這些類型,將定點數擴展到4個。修改vertices的值爲:

    var vertices = new Float32Array([
        -0.5, 0.5,
        -0.5, -0.5,
        0.5, 0.5,
        0.5, -0.5
    ]);

然後修改參數的值,就可以看到繪製出不同的圖形, 如下:

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

發佈了364 篇原創文章 · 獲贊 148 · 訪問量 89萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章