Skip to content

Commit 7ed526c

Browse files
committed
高级变换与动画基础
动画
1 parent ed706ed commit 7ed526c

File tree

3 files changed

+213
-2
lines changed

3 files changed

+213
-2
lines changed

docs/WebGL/Chapter2.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ outlineText: "33"
2121

2222
上一章中的方式只能绘制一个点。对于那些由多个顶点组成的图形,比如三角形、矩形和立方体来说,我们需要一次性地将图形的顶点全部传出顶点着色器,然后才能把图形画出来。
2323

24-
WebGL提供了提供了一种很方便的机制,即缓冲区对象(buffer object),他可以一次性地向着色器传入多个顶点的数据。缓冲区对象是`WebGL`系统中的一块内存区域,
24+
WebGL提供了一种很方便的机制,即缓冲区对象(buffer object),他可以一次性地向着色器传入多个顶点的数据。缓冲区对象是`WebGL`系统中的一块内存区域,
2525
我们可以一次性地向缓冲区对象中填充大量的顶点数,然后将这些数据保存在其中供`顶点着色器`使用。
2626

2727
```js

docs/WebGL/Chapter3.md

Lines changed: 212 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,4 +374,215 @@ modelMatrix.translate(Tx, 0, 0) // 将模型矩阵乘以平移矩阵
374374

375375
## 动画
376376

377-
将矩阵变换运用到动画图形中去
377+
将矩阵变换运用到动画图形中去
378+
379+
380+
### 动画基础
381+
382+
为了让一个三角形转动起来,需要做到是:不断擦除和重绘三角形,并且在每次重绘时轻微地改变角度
383+
384+
385+
```js
386+
const VSHADER_SOURCE =
387+
'attribute vec4 a_Position;\n' +
388+
'uniform mat4 u_ModelMatrix;\n' +
389+
'void main() {\n' +
390+
'gl_Position = u_ModelMatrix * a_Position;\n' +
391+
// 'gl_PointSize = 10.0;\n' +
392+
'}\n'
393+
const FSHADER_SOURCE =
394+
' void main() {\n' +
395+
'gl_FragColor = vec4(1.0, 1.0, 0.0,1.0);\n' +
396+
'}\n'
397+
398+
399+
var ANGLE = 90.0
400+
401+
var ANGLE_STEP = 45.0 // [!code ++]
402+
403+
function main() {
404+
var canvas = document.getElementById('webgl')
405+
var gl = getWebGLContext(canvas)
406+
if (!gl) {
407+
console.error('Failed to get the rendering context for WebGL')
408+
return;
409+
}
410+
411+
// 初始化着色器
412+
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
413+
console.error('Failed to initialize shaders.')
414+
return;
415+
}
416+
417+
// 设置顶点着色器
418+
var n = initVertexBuffers(gl);
419+
420+
421+
422+
if (n < 0) {
423+
console.error('Failed to set the positions of the vertices')
424+
return;
425+
}
426+
427+
gl.clearColor(0.0, 0.0, 0.0, 1.0) // [!code ++]
428+
429+
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix')
430+
431+
var currentAngle = 0.0 // [!code ++]
432+
433+
var modelMatrix = new Matrix4()
434+
435+
var tick = function () { // [!code ++]
436+
currentAngle = animate(currentAngle) // 更新旋转角 // [!code ++]
437+
draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) // [!code ++]
438+
439+
requestAnimationFrame(tick) // [!code ++]
440+
441+
} // [!code ++]
442+
tick() // [!code ++]
443+
var Tx = 0.5
444+
445+
// 平移旋转
446+
modelMatrix.setRotate(ANGLE, 0, 0, 1)
447+
modelMatrix.translate(Tx, 0.5, 1)
448+
449+
// 旋转平移
450+
modelMatrix.setTranslate(Tx, 0.5, 1)
451+
modelMatrix.rotate(ANGLE, 0, 0, 1)
452+
453+
454+
455+
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements)
456+
457+
458+
459+
// 获取attribut变量的存储位置
460+
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
461+
//
462+
// var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor')
463+
if (a_Position < 0) {
464+
console.error('Failed to get the storage location of a_Position')
465+
return;
466+
}
467+
468+
canvas.onmousedown = function (ev) {click(ev, gl, canvas, a_Position, u_FragColor)}
469+
470+
471+
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)
472+
473+
// 设置canvas背景色
474+
gl.clearColor(0.0, 0.0, 0.0, 1.0)
475+
476+
// 清空canvas
477+
gl.clear(gl.COLOR_BUFFER_BIT);
478+
479+
// 绘制三个点
480+
gl.drawArrays(gl.TRIANGLES, 0, n)
481+
482+
}
483+
484+
function draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) { // [!code ++]
485+
// 设置旋转矩阵 // [!code ++]
486+
modelMatrix.setRotate(currentAngle, 0, 0, 1) // [!code ++]
487+
// 将旋转矩阵传输给顶点着色器 // [!code ++]
488+
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements) // [!code ++]
489+
490+
// 清除 canvas // [!code ++]
491+
gl.clear(gl.COLOR_BUFFER_BIT) // [!code ++]
492+
493+
// 绘制三角形 // [!code ++]
494+
495+
gl.drawArrays(gl.TRIANGLES, 0, n) // [!code ++]
496+
} // [!code ++]
497+
498+
var g_last = Date.now() // [!code ++]
499+
500+
function animate(angle) { // [!code ++]
501+
var now = Date.now() // [!code ++]
502+
503+
var elapsed = now - g_last; // [!code ++]
504+
505+
g_last = now // [!code ++]
506+
507+
var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0 // [!code ++]
508+
509+
return newAngle %= 360 // [!code ++]
510+
} // [!code ++]
511+
512+
function initVertexBuffers(gl) {
513+
var vertices = new Float32Array([
514+
-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5
515+
])
516+
var n = 4 // 点的个数
517+
518+
// 创建缓冲区对象
519+
var vertexBuffer = gl.createBuffer();
520+
if (!vertexBuffer) {
521+
console.error('Failed to create the buffer object')
522+
return -1;
523+
}
524+
525+
// 将缓冲区对象绑定到目标
526+
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
527+
528+
// 向缓冲区对象中写入数据
529+
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
530+
531+
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
532+
533+
// 将缓冲区对象分配给a_Position变量
534+
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
535+
536+
// 连接a_Position变量与分配给它的缓冲对象
537+
gl.enableVertexAttribArray(a_Position)
538+
return n
539+
}
540+
541+
```
542+
543+
![效果图](./images/xxt.gif)
544+
545+
**为了是三角形动起来,需要反复进行两步操作**
546+
547+
1. 更新三角形的当前角度 currentTriangle
548+
2. 调用绘制函数,根据当前角度绘制三角形
549+
550+
### 请求再次被调用(requestAnimationFrame())
551+
552+
如果想要Javascript重复执行某个特定的任务,可以使用`setInterval()`函数
553+
554+
`setInterval(func, delay)`
555+
| 参数 | 描述 |
556+
|-------|----------------|
557+
| func | 指定需要多次调用的函数 |
558+
| delay | 指定时间间隔(以毫秒为单位) |
559+
| 返回值 | Time id |
560+
561+
现代浏览器都支持多个标签页,每个标签页具有单独的Javascript运行环境,但是自`setInterval()`函数诞生之初,浏览器还没有开始支持多标签页。所以在现代浏览器中,
562+
不管是标签页是否被激活,其中的`setInterval()`函数都会反复调用func,如果标签页比较多,就会增加浏览器的负荷(现在浏览器有优化了,失活时间太长,它调用的次数会被减少)。所有后来,浏览器又引入了`requestAnimation()`方法,
563+
该方法只有当标签页处于激活状态时才会生效。`requestAnimationFrame()`是新引入的方法。
564+
565+
**requestAnimationFrame()**
566+
当调用`requestAnimationFrame()`函数时,即是在告诉浏览器在将来的某个时间调用作为第一个参数的函数。
567+
568+
`requestAnimationFrame(func)`
569+
570+
| 参数 | 描述 |
571+
|------|-------------------------------------------|
572+
| func | 指定将来某个时刻调用的函数。函数将会接收一个time参数,用来表示此次调用的时间戳 |
573+
| 返回值 | Request id |
574+
575+
576+
这个函数的好处是可以避免在未激活的标签页上运行动画。
577+
578+
但是无法指定重复调用的间隔;函数func会在浏览器需要网页的某个元素重绘时被调用。此外还需要注意,在浏览器成功地调用了一次func后,想要再次被调用,就必须要再次发起请求,因为前一次请求已经结束。(也就是说,requestAnimationFrame更像setTimeOut而不是setInterval,
579+
不会因为你发起一次请求,就会不停地循环调用func)。在调用函数后,需要发出下次调用的请求,因为上一次关于调用的请求在调用完成之后就结束了使命。
580+
581+
`cancelAnimationFrame()`
582+
583+
取消由`requestAnimationFrame()`发起的请求
584+
585+
| 参数 | 描述 |
586+
|-----------|-------------------------------|
587+
| requestID | 指定requestAnimationFrame()的返回值 |
588+
| 返回值 ||

docs/WebGL/images/xxt.gif

647 KB
Loading

0 commit comments

Comments
 (0)