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

示例可以到这里查看。


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

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

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

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

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

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

顶点着色器(vertex shader):

1
2
3
4
5
6
7
8
9
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):

1
2
3
4
5
6
7
8
9
#ifdef GL_ES
precision mediump float;
#endif

varying vec4 v_Color;

void main() {
gl_FragColor = v_Color;
}

逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/**
* 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();
分享到 评论