示例可以到这里查看。
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();