- 原文地址:Day 13. Simple animation
- 原文作者:Andrei Lesnitsky
这是 WebGL 系列的第 13 天教程,每天都有新文章公布。
订阅或者退出邮件列表以便及时获取更新内容。
源代码在这里
以前的所有教程都是基于动态图像,当初让咱们增加动态效果!
咱们须要一个简略的顶点着色器
???? src/shaders/rotating-square.v.glsl
attribute vec2 position;
uniform vec2 resolution;
void main() {gl_Position = vec4(position / resolution * 2.0 - 1.0, 0, 1);
}
还有片段着色器
???? src/shaders/rotating-square.f.glsl
precision mediump float;
void main() {gl_FragColor = vec4(1, 0, 0, 1);
}
更新 JS 文件
???? index.html
</head>
<body>
<canvas></canvas>
- <script src="./dist/texture.js"></script>
+ <script src="./dist/rotating-square.js"></script>
</body>
</html>
???? src/rotating-square.js
import vShaderSource from './shaders/rotating-square.v.glsl';
import fShaderSource from './shaders/rotating-square.f.glsl';
???? webpack.config.js
entry: {
'week-1': './src/week-1.js',
'texture': './src/texture.js',
+ 'rotating-square': './src/rotating-square.js',
},
output: {
获取 WebGL 上下文
???? src/rotating-square.js
import vShaderSource from './shaders/rotating-square.v.glsl';
import fShaderSource from './shaders/rotating-square.f.glsl';
+
+ const canvas = document.querySelector('canvas');
+ const gl = canvas.getContext('webgl');
+
使画布填满页面
???? src/rotating-square.js
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
+ const width = document.body.offsetWidth;
+ const height = document.body.offsetHeight;
+
+ canvas.width = width * devicePixelRatio;
+ canvas.height = height * devicePixelRatio;
+
+ canvas.style.width = `${width}px`;
+ canvas.style.height = `${height}px`;
创立着色器
???? src/rotating-square.js
import vShaderSource from './shaders/rotating-square.v.glsl';
import fShaderSource from './shaders/rotating-square.f.glsl';
+ import {compileShader} from './gl-helpers';
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
+
+ const vShader = gl.createShader(gl.VERTEX_SHADER);
+ const fShader = gl.createShader(gl.FRAGMENT_SHADER);
+
+ compileShader(gl, vShader, vShaderSource);
+ compileShader(gl, fShader, fShaderSource);
创立程序 program
???? src/rotating-square.js
compileShader(gl, vShader, vShaderSource);
compileShader(gl, fShader, fShaderSource);
+
+ const program = gl.createProgram();
+
+ gl.attachShader(program, vShader);
+ gl.attachShader(program, fShader);
+
+ gl.linkProgram(program);
+ gl.useProgram(program);
获取属性 attribute
和 uniform
坐标地位
???? src/rotating-square.js
import vShaderSource from './shaders/rotating-square.v.glsl';
import fShaderSource from './shaders/rotating-square.f.glsl';
- import {compileShader} from './gl-helpers';
+ import {setupShaderInput, compileShader} from './gl-helpers';
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
gl.linkProgram(program);
gl.useProgram(program);
+
+ const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
创立顶点以便绘制正方形
???? src/rotating-square.js
import vShaderSource from './shaders/rotating-square.v.glsl';
import fShaderSource from './shaders/rotating-square.f.glsl';
import {setupShaderInput, compileShader} from './gl-helpers';
+ import {createRect} from './shape-helpers';
+ import {GLBuffer} from './GLBuffer';
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
gl.useProgram(program);
const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
+
+ const vertexPositionBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array([+ ...createRect(canvas.width / 2 - 100, canvas.height / 2 - 100, 200, 200),
+ ]), gl.STATIC_DRAW);
设置属性 attribute
指针
???? src/rotating-square.js
const vertexPositionBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array([...createRect(canvas.width / 2 - 100, canvas.height / 2 - 100, 200, 200),
]), gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);
创立索引缓冲区
???? src/rotating-square.js
]), gl.STATIC_DRAW);
gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);
+
+ const indexBuffer = new GLBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([
+ 0, 1, 2,
+ 1, 2, 3,
+ ]), gl.STATIC_DRAW);
调制分辨率并设置视口 viewport
???? src/rotating-square.js
0, 1, 2,
1, 2, 3,
]), gl.STATIC_DRAW);
+
+ gl.uniform2fv(programInfo.uniformLocations.resolution, [canvas.width, canvas.height]);
+
+ gl.viewport(0, 0, canvas.width, canvas.height);
并启动绘制调用性能
???? src/rotating-square.js
gl.uniform2fv(programInfo.uniformLocations.resolution, [canvas.width, canvas.height]);
gl.viewport(0, 0, canvas.width, canvas.height);
+ gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);
当初让咱们思考如何旋转这个正方形
实际上,咱们能够拟合到圆中,并且能够应用 radius
、cos
和 sin
来计算每个顶点的坐标,咱们须要做的是向每个顶点减少一些角度。
让咱们从角度动手重构咱们的 createRect
助手。
???? src/rotating-square.js
const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
const vertexPositionBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array([- ...createRect(canvas.width / 2 - 100, canvas.height / 2 - 100, 200, 200),
+ ...createRect(canvas.width / 2 - 100, canvas.height / 2 - 100, 200, 200, 0),
]), gl.STATIC_DRAW);
gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);
???? src/shape-helpers.js
- export function createRect(top, left, width, height) {
+ const Pi_4 = Math.PI / 4;
+
+ export function createRect(top, left, width, height, angle = 0) {
+ const centerX = width / 2;
+ const centerY = height / 2;
+
+ const diagonalLength = Math.sqrt(centerX ** 2 + centerY ** 2);
+
+ const x1 = centerX + diagonalLength * Math.cos(angle + Pi_4);
+ const y1 = centerY + diagonalLength * Math.sin(angle + Pi_4);
+
+ const x2 = centerX + diagonalLength * Math.cos(angle + Pi_4 * 3);
+ const y2 = centerY + diagonalLength * Math.sin(angle + Pi_4 * 3);
+
+ const x3 = centerX + diagonalLength * Math.cos(angle - Pi_4);
+ const y3 = centerY + diagonalLength * Math.sin(angle - Pi_4);
+
+ const x4 = centerX + diagonalLength * Math.cos(angle - Pi_4 * 3);
+ const y4 = centerY + diagonalLength * Math.sin(angle - Pi_4 * 3);
+
return [
- left, top, // x1 y1
- left + width, top, // x2 y2
- left, top + height, // x3 y3
- left + width, top + height, // x4 y4
+ x1 + left, y1 + top,
+ x2 + left, y2 + top,
+ x3 + left, y3 + top,
+ x4 + left, y4 + top,
];
}
当初咱们须要定义初始角度
???? src/rotating-square.js
gl.uniform2fv(programInfo.uniformLocations.resolution, [canvas.width, canvas.height]);
gl.viewport(0, 0, canvas.width, canvas.height);
- gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);
+
+ let angle = 0;
以及每帧都会调用的函数
???? src/rotating-square.js
gl.viewport(0, 0, canvas.width, canvas.height);
let angle = 0;
+
+ function frame() {+ requestAnimationFrame(frame);
+ }
+
+ frame();
WebGL 会遍历顶点数据并进行渲染,咱们须要更新此数据使其出现不同成果。
???? src/rotating-square.js
let angle = 0;
function frame() {
+ vertexPositionBuffer.setData(
+ gl,
+ new Float32Array(+ createRect(canvas.width / 2 - 100, canvas.height / 2 - 100, 200, 200, angle)
+ ),
+ gl.STATIC_DRAW,
+ );
+
requestAnimationFrame(frame);
}
咱们还须要更新每帧的旋转角度
???? src/rotating-square.js
gl.STATIC_DRAW,
);
+ angle += Math.PI / 60;
+
requestAnimationFrame(frame);
}
还要调用绘制函数
???? src/rotating-square.js
angle += Math.PI / 60;
+ gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);
requestAnimationFrame(frame);
}
酷!咱们当初有一个旋转的正方形!????
应用旋转矩阵能够简化咱们刚刚实现的操作
如果不太纯熟线性代数也不必放心,这里有一个非凡的程序工具包????
???? package.json
"webpack-cli": "^3.3.5"
},
"dependencies": {
+ "gl-matrix": "^3.0.0",
"glsl-extract-sync": "0.0.0"
}
}
咱们须要定义一个旋转矩阵
???? src/shaders/rotating-square.v.glsl
attribute vec2 position;
uniform vec2 resolution;
+ uniform mat2 rotationMatrix;
+
void main() {gl_Position = vec4(position / resolution * 2.0 - 1.0, 0, 1);
}
并乘以顶点坐标地位
???? src/shaders/rotating-square.v.glsl
uniform mat2 rotationMatrix;
void main() {- gl_Position = vec4(position / resolution * 2.0 - 1.0, 0, 1);
+ gl_Position = vec4((position / resolution * 2.0 - 1.0) * rotationMatrix, 0, 1);
}
当初咱们能够解脱顶点地位更新影响
???? src/rotating-square.js
import {setupShaderInput, compileShader} from './gl-helpers';
import {createRect} from './shape-helpers';
import {GLBuffer} from './GLBuffer';
+ import {mat2} from 'gl-matrix';
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
gl.viewport(0, 0, canvas.width, canvas.height);
- let angle = 0;
+ const rotationMatrix = mat2.create();
function frame() {
- vertexPositionBuffer.setData(
- gl,
- new Float32Array(- createRect(canvas.width / 2 - 100, canvas.height / 2 - 100, 200, 200, angle)
- ),
- gl.STATIC_DRAW,
- );
-
- angle += Math.PI / 60;
gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);
requestAnimationFrame(frame);
并改用旋转矩阵
???? src/rotating-square.js
const rotationMatrix = mat2.create();
function frame() {+ gl.uniformMatrix2fv(programInfo.uniformLocations.rotationMatrix, false, rotationMatrix);
+
+ mat2.rotate(rotationMatrix, rotationMatrix, -Math.PI / 60);
gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);
requestAnimationFrame(frame);
论断
事实证明,在咱们的形态助手重构中,看起来很简单的数学能够通过矩阵运算轻松实现。GPU 执行矩阵乘法的速度十分快(对于此类操作,它在硬件级别上有非凡的优化),因而能够应用变换矩阵进行很多变换。这是十分重要的概念,尤其是在 3d 渲染世界中。
明天就这样,今天见!????