WebGL初學:構建一個多彩三角形與矩形

weixin_33860553發表於2018-05-24

最近老學了一門新的課程:WebGL。實驗一就是利用WebGL繪製一個頂點顏色不同的三角形和矩形,就像下面這樣:


2305391-9b26e4b7621d0b0b.png
矩形.png

2305391-e26a606fba8c83bf.png
三角形.png

直接上程式碼吧:
HTML頁面:使用canvas元素生成畫布。

<canvas id="webgl-1" width="640" height="480">
        Your browser doesn't appear to support the HTML5 <code>&lt;canvas&gt;</code> element.
</canvas>
<br>
<canvas id="webgl-2" width="640" height="480">
        Your browser doesn't appear to support the HTML5 <code>&lt;canvas&gt;</code> element.
</canvas>

這裡我是直接用了兩個canvas元素以提供兩個不同形狀的繪製。
然後是javascript程式碼。
利用WebGL需要使用著色器,這裡不多介紹。
1.從HTML頁面獲取canvas元素的上下文,這裡主要要獲取到gl物件:

function initWebGL(canvas) {
    var gl;
    try {
        gl = canvas.getContext("webgl");
        gl.viewport(0, 0, canvas.width, canvas.height);
    } catch (e) {
        var msg = "無法從canvas中獲取webgl!" + e.toString();
        alert(msg);
        throw Error(msg);
    }

    return gl;
}

2.著色器原始碼:

// =======================================
// 頂點著色器和片元著色器
// =======================================
var vsSource = `
attribute vec3 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;

void main() {
  gl_Position = vec4(a_Position, 1.0);
  v_Color = a_Color;
}
`;

var fsSource = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;

void main() {
  gl_FragColor = v_Color;
}
`;

3.需要對著色器進行編譯及使用:

function loadShader(gl, type, source) {
    // 1.建立著色器物件
    var shader;
    if(type === "vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else if(type === "fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    }

    // 2.向著色器物件中填充著色器程式的原始碼
    gl.shaderSource(shader, source);

    // 3.編譯著色器
    gl.compileShader(shader);
    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
        var error = gl.getShaderInfoLog(shader);
        console.log('Failed to compile shader: ' + error);
        gl.deleteShader(shader);
        return -1;
    }

    // 4.返回著色器物件
    return shader;
}

function initShader(gl, vertSource, fragSource) {
    // 載入頂點著色器和片元著色器
    var vertShader = loadShader(gl, "vertex", vertSource);
    var fragShader = loadShader(gl, "fragment", fragSource);
    if(!vertShader || !fragShader) {
        return null;
    }
    // 1.建立著色器程式
    var shaderProgram = gl.createProgram();
    

    // 2.為程式物件分配著色器
    gl.attachShader(shaderProgram, vertShader);
    gl.attachShader(shaderProgram, fragShader);

    // 3.連線程式物件
    gl.linkProgram(shaderProgram);
    // 建立失敗, alert
    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      alert('無法初始化著色器程式: ' + gl.getProgramInfoLog(shaderProgram));
      console.log(gl.getProgramInfoLog(shaderProgram));
      return null;
    }

    // 4.使用程式物件
    gl.useProgram(shaderProgram);
    gl.program = shaderProgram;

    return true;
}

4.建立三角形物件:

function createTriangle(gl) {
    var verts = new Float32Array([
        0.0, 0.5,
        -0.5, -0.5,
        0.5, -0.5,
    ]);
    
    var colors = new Float32Array([
      1.0,  0.0,  0.0,
      0.0,  1.0,  0.0,
      0.0,  0.0,  1.0,
    ]);

    var vSIZE = verts.BYTES_PER_ELEMENT;
    var vertexBuffer = gl.createBuffer();
    var cSIZE = colors.BYTES_PER_ELEMENT;
    var colorBuffer = gl.createBuffer();

    var triangle = {
        vertsArray: verts,
        colorsArray: colors,
        vertBuffer: vertexBuffer,
        colorBuffer: colorBuffer,
        vSIZE: vSIZE,
        cSIZE: cSIZE,
        nVert: 3,
        primType: gl.TRIANGLES
    };

    return triangle;
}

5.類似地,建立矩形物件:

function createSquare(gl) {
    var verts = new Float32Array([
        0.5, 0.5,
        0.5, -0.5,
        -0.5, 0.5,
        -0.5, -0.5
    ]);
    
    var colors = new Float32Array([
      1.0,  0.0,  0.0,
      0.0,  1.0,  0.0,
      0.0,  0.0,  1.0,
      1.0,  1.0,  0.0
    ]);

    var vSIZE = verts.BYTES_PER_ELEMENT;
    var vertexBuffer = gl.createBuffer();

    var cSIZE = colors.BYTES_PER_ELEMENT;
    var colorBuffer = gl.createBuffer();

    var square = {
        vertsArray: verts,
        colorsArray: colors,
        vertBuffer: vertexBuffer,
        colorBuffer: colorBuffer,
        vSIZE: vSIZE,
        cSIZE: cSIZE,
        nVert: 4,
        primType: gl.TRIANGLE_STRIP
    };

    return square;
}

6.初始化快取:

function initBuffer(gl, obj) {
    gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, obj.vertsArray, gl.STATIC_DRAW);

    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, obj.vSIZE * 2 , 0);
    gl.enableVertexAttribArray(a_Position);

    gl.bindBuffer(gl.ARRAY_BUFFER, obj.colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, obj.colorsArray, gl.STATIC_DRAW);

    var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, obj.cSIZE * 3 , 0);
    gl.enableVertexAttribArray(a_Color);
}

7.主程式入口:

window.onload = function main() {
    var canvas1 = document.getElementById('webgl-1');
    var gl1 = initWebGL(canvas1);
    initShader(gl1, vsSource, fsSource);

    var obj1 = createSquare(gl1);
    initBuffer(gl1, obj1);
    drawScene(canvas1, gl1, obj1);
    
    var canvas2 = document.getElementById('webgl-2');
    var gl2 = initWebGL(canvas2);
    initShader(gl2, vsSource, fsSource);

    var obj2 = createTriangle(gl2);
    initBuffer(gl2, obj2);
    drawScene(canvas2, gl2, obj2);
};

8.完成!

相關文章