index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>02_绘制2D物体</title>
<script src="sylvester.js"></script>
<script src="glUtils.js"></script>
<script src="index.js"></script>
</head>
<body onload="start()">
<canvas id="glCanvas" width="640" height="480"></canvas>
<script id="shader-fs" type="x-shader/x-fragment">
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
}
</script>
</body>
</html>
index.js
var gl; // A global variable for the WebGL context
function start() {
var canvas = document.getElementById('glCanvas');
// Initialize the GL context
gl = initWebGL(canvas);
// Only continue if WebGL is available and working
if (!gl) {
return;
}
// Set clear color to black, fully opaque
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// Enable depth testing
gl.enable(gl.DEPTH_TEST);
// Near things obscure far things
gl.depthFunc(gl.LEQUAL);
// Clear the color as well as the depth buffer.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 画布大小改变后需要重新设置视口大小
gl.viewport(0, 0, canvas.width, canvas.height);
initShaders();
initBuffers();
drawScene();
}
function initWebGL(canvas) {
gl = null;
// Try to grab the standard context. If it fails, fallback to experimental.
gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
// If we don't have a GL context, give up now
if (!gl) {
alert('Unable to initialize WebGL. Your browser may not support it.');
}
return gl;
}
function initShaders() {
var fragmentShader = getShader(gl, 'shader-fs');
var vertexShader = getShader(gl, 'shader-vs');
// Create the shader program
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
// If creating the shader program failed, alert
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.log('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
}
gl.useProgram(shaderProgram);
vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
gl.enableVertexAttribArray(vertexPositionAttribute);
}
function getShader(gl, id, type) {
var shaderScript, theSource, currentChild, shader;
shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
theSource = shaderScript.text;
if (!type) {
if (shaderScript.type == 'x-shader/x-fragment') {
type = gl.FRAGMENT_SHADER;
} else if (shaderScript.type == 'x-shader/x-vertex') {
type = gl.VERTEX_SHADER;
} else {
// Unknown shader type
return null;
}
}
shader = gl.createShader(type);
gl.shaderSource(shader, theSource);
// Compile the shader program
gl.compileShader(shader);
// See if it compiled successfully
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
var horizAspect = 480.0 / 640.0;
function initBuffers() {
squareVerticesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
var vertices = [
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
perspectiveMatrix = makePerspective(45, 640.0 / 480.0, 0.1, 100.0);
loadIdentity();
mvTranslate([-0.0, 0.0, -6.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function loadIdentity() {
mvMatrix = Matrix.I(4);
}
function multMatrix(m) {
mvMatrix = mvMatrix.x(m);
}
function mvTranslate(v) {
multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
}
function setMatrixUniforms() {
var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten()));
var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten()));
}
glUtils.js
// augment Sylvester some
Matrix.Translation = function (v) {
if (v.elements.length == 2) {
var r = Matrix.I(3);
r.elements[2][0] = v.elements[0];
r.elements[2][1] = v.elements[1];
return r;
}
if (v.elements.length == 3) {
var r = Matrix.I(4);
r.elements[0][3] = v.elements[0];
r.elements[1][3] = v.elements[1];
r.elements[2][3] = v.elements[2];
return r;
}
throw "Invalid length for Translation";
}
Matrix.prototype.flatten = function () {
var result = [];
if (this.elements.length == 0)
return [];
for (var j = 0; j < this.elements[0].length; j++)
for (var i = 0; i < this.elements.length; i++)
result.push(this.elements[i][j]);
return result;
}
Matrix.prototype.ensure4x4 = function () {
if (this.elements.length == 4 &&
this.elements[0].length == 4)
return this;
if (this.elements.length > 4 ||
this.elements[0].length > 4)
return null;
for (var i = 0; i < this.elements.length; i++) {
for (var j = this.elements[i].length; j < 4; j++) {
if (i == j)
this.elements[i].push(1);
else
this.elements[i].push(0);
}
}
for (var i = this.elements.length; i < 4; i++) {
if (i == 0)
this.elements.push([1, 0, 0, 0]);
else if (i == 1)
this.elements.push([0, 1, 0, 0]);
else if (i == 2)
this.elements.push([0, 0, 1, 0]);
else if (i == 3)
this.elements.push([0, 0, 0, 1]);
}
return this;
};
Matrix.prototype.make3x3 = function () {
if (this.elements.length != 4 ||
this.elements[0].length != 4)
return null;
return Matrix.create([[this.elements[0][0], this.elements[0][1], this.elements[0][2]],
[this.elements[1][0], this.elements[1][1], this.elements[1][2]],
[this.elements[2][0], this.elements[2][1], this.elements[2][2]]]);
};
Vector.prototype.flatten = function () {
return this.elements;
};
function mht(m) {
var s = "";
if (m.length == 16) {
for (var i = 0; i < 4; i++) {
s += "<span style='font-family: monospace'>[" + m[i * 4 + 0].toFixed(4) + "," + m[i * 4 + 1].toFixed(4) + "," + m[i * 4 + 2].toFixed(4) + "," + m[i * 4 + 3].toFixed(4) + "]</span><br>";
}
} else if (m.length == 9) {
for (var i = 0; i < 3; i++) {
s += "<span style='font-family: monospace'>[" + m[i * 3 + 0].toFixed(4) + "," + m[i * 3 + 1].toFixed(4) + "," + m[i * 3 + 2].toFixed(4) + "]</font><br>";
}
} else {
return m.toString();
}
return s;
}
//
// gluLookAt
//
function makeLookAt(ex, ey, ez,
cx, cy, cz,
ux, uy, uz) {
var eye = $V([ex, ey, ez]);
var center = $V([cx, cy, cz]);
var up = $V([ux, uy, uz]);
var mag;
var z = eye.subtract(center).toUnitVector();
var x = up.cross(z).toUnitVector();
var y = z.cross(x).toUnitVector();
var m = $M([[x.e(1), x.e(2), x.e(3), 0],
[y.e(1), y.e(2), y.e(3), 0],
[z.e(1), z.e(2), z.e(3), 0],
[0, 0, 0, 1]]);
var t = $M([[1, 0, 0, -ex],
[0, 1, 0, -ey],
[0, 0, 1, -ez],
[0, 0, 0, 1]]);
return m.x(t);
}
//
// glOrtho
//
function makeOrtho(left, right,
bottom, top,
znear, zfar) {
var tx = -(right + left) / (right - left);
var ty = -(top + bottom) / (top - bottom);
var tz = -(zfar + znear) / (zfar - znear);
return $M([[2 / (right - left), 0, 0, tx],
[0, 2 / (top - bottom), 0, ty],
[0, 0, -2 / (zfar - znear), tz],
[0, 0, 0, 1]]);
}
//
// gluPerspective
//
function makePerspective(fovy, aspect, znear, zfar) {
var ymax = znear * Math.tan(fovy * Math.PI / 360.0);
var ymin = -ymax;
var xmin = ymin * aspect;
var xmax = ymax * aspect;
return makeFrustum(xmin, xmax, ymin, ymax, znear, zfar);
}
//
// glFrustum
//
function makeFrustum(left, right,
bottom, top,
znear, zfar) {
var X = 2 * znear / (right - left);
var Y = 2 * znear / (top - bottom);
var A = (right + left) / (right - left);
var B = (top + bottom) / (top - bottom);
var C = -(zfar + znear) / (zfar - znear);
var D = -2 * zfar * znear / (zfar - znear);
return $M([[X, 0, A, 0],
[0, Y, B, 0],
[0, 0, C, D],
[0, 0, -1, 0]]);
}
//
// glOrtho
//
function makeOrtho(left, right, bottom, top, znear, zfar) {
var tx = -(right + left) / (right - left);
var ty = -(top + bottom) / (top - bottom);
var tz = -(zfar + znear) / (zfar - znear);
return $M([[2 / (right - left), 0, 0, tx],
[0, 2 / (top - bottom), 0, ty],
[0, 0, -2 / (zfar - znear), tz],
[0, 0, 0, 1]]);
}