WebGL 测试1 - 绘制纯色边三角形

示例可以到这里查看。


Pixi 的数据结构不符合 Bulletproof 的使用要求,要调整的话还要考虑与原架构的兼容性(提供了一个抽象的 Graphics),想想其实也没用到多少其功能,我们有自己的体系,就补充上 WebGL 的一块好了。

对 WebGL 不熟(以前就没写过底层一点的 OpenGL 程序),所以对绘制的基本过程也有点困难。因此就阅读了《WebGL 编程指南》,了解原理。

简单来说,启动后会绘制一个三角形,边分别是红、绿、蓝;按着 Ctrl 拖动有惊喜。

这个示例受限于 ANGLE,可以看到线宽在 Windows 平台上默认是不起作用的。为什么在 Pixi 中起作用似乎是因为它将边当成矩形来绘制(每一条边两个三角形),仔细看一下再说。

着色器这一块基本上可以用公用着色器,所以最终变成了操作顶点数据。

Matrix4 使用的是原书附的代码,作者是 Takafumi Kanda 和 Kouichi Matsuda。

顶点着色器(vertex shader):

attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
uniform mat4 u_ProjMatrix;

void main() {
    gl_Position = u_ProjMatrix * a_Position;
    v_Color = a_Color;
}

片元着色器(fragment shader):

#ifdef GL_ES
precision mediump float;
#endif

varying vec4 v_Color;

void main() {
    gl_FragColor = v_Color;
}

逻辑:

/**
 * Created by MIC on 2015/10/6.
 */

"use strict";

/**
 * @type {HTMLCanvasElement}
 */
var canvas = document.getElementById("canvas");
/**
 * @type {WebGLRenderingContext}
 */
var glc = canvas.getContext("webgl", {antialias: 1, alpha: 1, depth: 1});
var gl = WebGLRenderingContext;

// X, Y, R, G, B
/**
 * @type {Number[]}
 */
var verticesAndColorsArray = [
    0.0, 50.0, 1.0, 0.0, 0.0,
    -50.0, -50.0, 1.0, 0.0, 0.0,
    -50.0, -50.0, 0.0, 1.0, 0.0,
    50.0, -50.0, 0.0, 1.0, 0.0,
    50.0, -50.0, 0.0, 0.0, 1.0,
    0.0, 50.0, 0.0, 0.0, 1.0
];

/**
 * @param {WebGLRenderingContext} glc
 * @param {String} vsid
 * @param {String} fsid
 * @returns {Boolean}
 */
function initShaders(glc, vsid, fsid) {
    var vssource = getShaderSource(vsid);
    var fssource = getShaderSource(fsid);
    var program = createProgram(glc, vssource, fssource);
    if (!program) {
        console.log("Failed to create program.");
        return false;
    }
    glc.useProgram(program);
    glc.program = program;
    return true;

    /**
     * @param {WebGLRenderingContext} glc
     * @param {String} vshader
     * @param {String} fshader
     * @returns {WebGLProgram}
     */
    function createProgram(glc, vshader, fshader) {
        var vertexShader = loadShader(glc, gl.VERTEX_SHADER, vshader);
        var fragmentShader = loadShader(glc, gl.FRAGMENT_SHADER, fshader);
        if (!vertexShader || !fragmentShader) {
            return null;
        }
        var program = glc.createProgram();
        if (!program) {
            return null;
        }
        glc.attachShader(program, vertexShader);
        glc.attachShader(program, fragmentShader);
        glc.linkProgram(program);
        var linked = glc.getProgramParameter(program, gl.LINK_STATUS);
        if (!linked) {
            var errorLog = glc.getProgramInfoLog(program);
            console.log("Failed to link program: " + errorLog);
            glc.deleteProgram(program);
            glc.deleteShader(vertexShader);
            glc.deleteShader(fragmentShader);
            return null;
        }
        return program;
    }

    /**
     * @param {String} id
     * @returns {String}
     */
    function getShaderSource(id) {
        /**
         * @type {HTMLElement}
         */
        var elem = document.getElementById(id);
        return elem.textContent;
    }

    /**
     * @param {WebGLRenderingContext} glc
     * @param {Number} type
     * @param {String} source
     * @returns {WebGLShader}
     */
    function loadShader(glc, type, source) {
        var shader = glc.createShader(type);
        if (shader === null) {
            console.log("Unable to create shader.");
            return null;
        }
        glc.shaderSource(shader, source);
        glc.compileShader(shader);
        var isCompiled = glc.getShaderParameter(shader, gl.COMPILE_STATUS);
        if (!isCompiled) {
            var errorLog = glc.getShaderInfoLog(shader);
            console.log("Failed to compile shader: " + errorLog);
            glc.deleteShader(shader);
            return null;
        }
        return shader;
    }
}

/**
 * @param {WebGLRenderingContext} glc
 * @returns {Number}
 */
function initVertexBuffers(glc) {
    /**
     * @type {Float32Array}
     */
    var verticesAndColors = new Float32Array(verticesAndColorsArray);
    /**
     * @type {Number}
     */
    var n = verticesAndColorsArray.length / 5;

    var vertexColorBuffer = glc.createBuffer();
    if (!vertexColorBuffer) {
        console.log("Failed to create the buffer object.");
        return 0;
    }
    glc.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
    glc.bufferData(gl.ARRAY_BUFFER, verticesAndColors, gl.STATIC_DRAW);
    /**
     * @type {Number}
     */
    var FSIZE = Float32Array.BYTES_PER_ELEMENT;
    var a_Position = glc.getAttribLocation(glc.program, "a_Position");
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return -1;
    }
    glc.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0);
    glc.enableVertexAttribArray(a_Position);
    var a_Color = glc.getAttribLocation(glc.program, "a_Color");
    if (a_Color < 0) {
        console.log("Failed to get the storage location of a_Color");
        return -1;
    }
    glc.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2);
    glc.enableVertexAttribArray(a_Color);
    glc.bindBuffer(gl.ARRAY_BUFFER, null);
    return n;
}

/**
 * @param {WebGLRenderingContext} glc
 */
function clearCanvas(glc) {
    glc.clearColor(0, 0, 0, 0);
    glc.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}

/**
 * @param {WebGLRenderingContext} glc
 */
function drawLines(glc) {
    /**
     * @type {Number}
     */
    var n;
    if ((n = initVertexBuffers(glc)) <= 0) {
        console.log("Failed to initialize vertex buffers.");
        return;
    }
    clearCanvas(glc);
    // 考虑到以后是按照不同的线条和填充来分组绘制的,这里用 gl.LINE_STRIP 来压缩数组也不错
    glc.drawArrays(gl.LINES, 0, n);
}

function main() {
    if (!initShaders(glc, "vshader-color", "fshader-color")) {
        console.log("Failed to intialize shaders.");
        return;
    }

    glc.lineWidth(5);

    var u_ProjMatrix = glc.getUniformLocation(glc.program, "u_ProjMatrix");
    if (!u_ProjMatrix) {
        console.log("Failed to get the storage location of u_ProjMatrix.");
        return;
    }
    var projMatrix = new Matrix4();
    projMatrix.setOrtho(-canvas.width / 2, canvas.width / 2, -canvas.height / 2, canvas.height / 2, 0, 1000);
    glc.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

    drawLines(glc);

    canvas.addEventListener("mousemove", canvasOnMouseMove);
}

/**
 * @param {MouseEvent} ev
 */
function canvasOnMouseMove(ev) {
    if (ev.ctrlKey) {
        // x, y, r, g, b
        var arr = [0, 0, 0, 0, 0];
        var lastArr = [0, 0, 0, 0, 0];
        var len = verticesAndColorsArray.length;
        lastArr[0] = verticesAndColorsArray[len - 1 - 4];
        lastArr[1] = verticesAndColorsArray[len - 1 - 3];
        for (var i = 0; i < 3; i++) {
            arr[i + 2] = Math.random();
            lastArr[i + 2] = arr[i + 2];
        }
        arr[0] = ev.offsetX - canvas.width / 2;
        arr[1] = -(ev.offsetY - canvas.height / 2);
        verticesAndColorsArray = verticesAndColorsArray.concat(lastArr).concat(arr);
        drawLines(glc);
    }
}

main();
分享到 评论