- 原文地址: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 渲染世界中。
明天就这样,今天见!????