(1) 示范代码1为交互实例:在鼠标点击的位置上绘制出点;示范代码2为动画实例:三角形按照恒定的速度(45度/秒)旋转。结合示范代码,学习理解交互与动画的基本思想与实现; (2) 结合示范代码1,将示范代码2改为根据鼠标来控制三角形的旋转;
示范代码1的结果如下图所示:
示范代码2如下图所示:
请根据教材内容、网络资源及示范代码,简单分析下交互与动画的实现原理与方法。
gl-matrix.js 下载地址:http://oty0nwcbq.bkt.clouddn.com/gl-matrix.js
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Draw a point with a mouse click</title>
</head>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 a_Position;
void main() {
gl_Position = a_Position ;
gl_PointSize = 10.0;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<body onload="startup()">
<canvas id="myGLCanvas" width="600" height="600">
</canvas>
</body>
<script type="text/javascript" src="gl-matrix.js"></script>
<script type="text/javascript" src="ClickedPoints.js"></script>
</html>
var gl;
function startup(){
var canvas = document.getElementById('myGLCanvas');//获取<canvas>元素
gl = createGLContext(canvas);
setupShaders();
// // Get the storage location of a_Position
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// Register function (event handler) to be called on a mouse press
canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position); };
// Specify the color for clearing <canvas>
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// Clear <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
}
var g_points = []; // The array for the position of a mouse press
function click(ev, gl, canvas, a_Position) {
var x = ev.clientX; // x coordinate of a mouse pointer
var y = ev.clientY; // y coordinate of a mouse pointer
var rect = ev.target.getBoundingClientRect() ;
x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);
// Store the coordinates to g_points array
g_points.push(x); g_points.push(y);
// Clear <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
var len = g_points.length;
for(var i = 0; i < len; i += 2) {
// Pass the position of a point to a_Position variable
gl.vertexAttrib3f(a_Position, g_points[i], g_points[i+1], 0.0);
// Draw
gl.drawArrays(gl.POINTS, 0, 1);
}
}
function createGLContext(canvas) {
var names = ["webgl", "experimental-webgl"];
var context = null;
for (var i=0; i < names.length; i++) {
try {
context = canvas.getContext(names[i]); //获取webgl context绘图上下文
} catch(e) {}
if (context) {
break;
}
}
if (context) {
context.viewportWidth = canvas.width;
context.viewportHeight = canvas.height;
} else {
alert("Failed to create WebGL context!");
}
return context;
}
function setupShaders() {
var vertexShader = loadShader(gl.VERTEX_SHADER, "vertex-shader");
var fragmentShader = loadShader(gl.FRAGMENT_SHADER, "fragment-shader");
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Failed to setup shaders");
}
gl.useProgram(shaderProgram);
gl.program= shaderProgram;
}
function loadShader(type, ShaderId) {
var shaderScript = document.getElementById( ShaderId );
var shader = gl.createShader(type);
gl.shaderSource( shader, shaderScript.text );
gl.compileShader( shader );
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("Error compiling shader" + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Continually Rotate A Triangle</title>
</head>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 a_Position;
uniform mat4 u_ModelMatrix;
void main() {
gl_Position = u_ModelMatrix * a_Position;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<body onload="startup()">
<canvas id="myGLCanvas" width="600" height="600">
</canvas>
</body>
<script type="text/javascript" src="gl-matrix.js"></script>
<script type="text/javascript" src="RotatingTriangle.js">
</script>
</html>
var gl;
// Rotation angle (degrees/second)
var ANGLE_STEP = 45.0;
var modelMatrixI = mat4.create();
function startup(){
var canvas = document.getElementById('myGLCanvas');//获取<canvas>元素
gl = createGLContext(canvas);
setupShaders();
// Write the positions of vertices to a vertex shader
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// Specify the color for clearing <canvas>
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// Get storage location of u_ModelMatrix
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
if (!u_ModelMatrix) {
console.log('Failed to get the storage location of u_ModelMatrix');
return;
}
// Current rotation angle
var currentAngle = 0.0;
// Model matrix
var modelMatrix = mat4.create();
// Start drawing
var tick = function() {
currentAngle = animate(currentAngle); // Update the rotation angle
draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix); // Draw the triangle
requestAnimationFrame(tick, canvas); // Request that the browser calls tick
};
tick();
}
function initVertexBuffers(gl) {
var vertices = new Float32Array ([
0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
var n = 3; // The number of vertices
// Create a buffer object
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// Bind the buffer object to target
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// Write date into the buffer object
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Assign the buffer object to a_Position variable
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if(a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// Enable the assignment to a_Position variable
gl.enableVertexAttribArray(a_Position);
return n;
}
function draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) {
// Set the rotation matrix
//modelMatrix.setRotate(currentAngle, 0, 0, 1); // Rotation angle, rotation axis (0, 0, 1)
var radian = Math.PI * currentAngle / 180.0; // Convert to radians
mat4.rotate(modelMatrix, modelMatrixI, radian, [0, 0 , 1]);
// Pass the rotation matrix to the vertex shader
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix);
// Clear <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw the rectangle
gl.drawArrays(gl.TRIANGLES, 0, n);
}
// Last time that this function was called
var g_last = Date.now();
function animate(angle) {
// Calculate the elapsed time
var now = Date.now();
var elapsed = now - g_last;
g_last = now;
// Update the current rotation angle (adjusted by the elapsed time)
var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
return newAngle %= 360;
}
function createGLContext(canvas) {
var names = ["webgl", "experimental-webgl"];
var context = null;
for (var i=0; i < names.length; i++) {
try {
context = canvas.getContext(names[i]); //获取webgl context绘图上下文
} catch(e) {}
if (context) {
break;
}
}
if (context) {
context.viewportWidth = canvas.width;
context.viewportHeight = canvas.height;
} else {
alert("Failed to create WebGL context!");
}
return context;
}
function setupShaders() {
var vertexShader = loadShader(gl.VERTEX_SHADER, "vertex-shader");
var fragmentShader = loadShader(gl.FRAGMENT_SHADER, "fragment-shader");
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Failed to setup shaders");
}
gl.useProgram(shaderProgram);
gl.program= shaderProgram;
}
function loadShader(type, ShaderId) {
var shaderScript = document.getElementById( ShaderId );
var shader = gl.createShader(type);
gl.shaderSource( shader, shaderScript.text );
gl.compileShader( shader );
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("Error compiling shader" + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}