WebGl 二维旋转及缩放

WebGl 二维旋转及缩放

本文基于上一篇- 《WebGl绘制多个图形》 的示例代码修改

添加 uniform 类型为vec2a_Rotation

利用数学公式

1
2
rotatedX = a_position.x * u_rotation.y + a_position.y * u_rotation.x;
rotatedY = a_position.y * u_rotation.y - a_position.x * u_rotation.x;

将图形进行旋转

1
2
3
4
5
6
7
8
9
10
11
12
attribute vec2 a_Position;
attribute float a_PointSize;
uniform vec2 a_Rotation;
void main(){
vec2 rotatedPosition = vec2(
a_Position.x * a_Rotation.y + a_Position.y * a_Rotation.x,
a_Position.y * a_Rotation.y - a_Position.x * a_Rotation.x
);

gl_Position = vec4(rotatedPosition * vec2(1, -1), 0, 1);
gl_PointSize=a_PointSize;
}

关于原理, 本文参考 webgl lessons

假如你想旋转一个矩形,在开始旋转之前矩形右上角坐标是 3.0, 9.0 , 让我们在单位圆上以十二点方向为起点顺时针旋转30度后取一个点。

圆上该点的位置是 0.50 和 0.87

1
2
3.0 * 0.87 + 9.0 * 0.50 = 7.1
9.0 * 0.87 - 3.0 * 0.50 = 6.3

这个结果正好是我们需要的结果

顺时针60度也一样

圆上该点的位置是 0.87 和 0.50 。

1
2
3.0 * 0.50 + 9.0 * 0.87 = 9.3
9.0 * 0.50 - 3.0 * 0.87 = 1.9

你会发现在我们顺时针旋转到右边的过程中,X 变大 Y 变小。如果我们继续旋转超过90 度后,X 变小 Y 变大,这种形式形成了旋转。

单位圆上的点还有一个名字,叫做正弦和余弦。所以对于任意给定角, 我们只需要求出正弦和余弦,像这样

1
2
3
4
5
6
function printSineAndCosineForAnAngle(angleInDegrees) {
var angleInRadians = angleInDegrees * Math.PI / 180;
var s = Math.sin(angleInRadians);
var c = Math.cos(angleInRadians);
console.log("s = " + s + " c = " + c);
}

示例代码

每5毫秒刷新一次画布,自动旋转二维图形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import vsSource from '~/index.vs';
import fsSource from '~/index.fs';

const t = arr => arr.map(x => x / 200);
const createSquare = (width, height, start) => {
const [x, y] = start;
// prettier-ignore
return [
x , y,
x + width, y,
x, y + height,
x + width, y + height
];
};

const canvas = document.getElementById('webgl');
const gl = canvas.getContext('webgl');

// 创建顶点着色器对象
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 绑定资源
gl.shaderSource(vertexShader, vsSource);
// 编译着色器
gl.compileShader(vertexShader);

// 创建片段着色器对象
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 绑定资源
gl.shaderSource(fragmentShader, fsSource);
// 编译着色器
gl.compileShader(fragmentShader);

// 创建一个着色器程序
const glProgram = gl.createProgram();

// 把前面创建的二个着色器对象添加到着色器程序中
gl.attachShader(glProgram, vertexShader);
gl.attachShader(glProgram, fragmentShader);

// 把着色器程序链接成一个完整的程序
gl.linkProgram(glProgram);

// 使用这个完整的程序
gl.useProgram(glProgram);

// 定位变量位置
const a_Position = gl.getAttribLocation(glProgram, 'a_Position');

// 定义角度
const a_Rotation = gl.getUniformLocation(glProgram, 'a_Rotation');

// prettier-ignore
const vtxData = [
createSquare(200,30, [-100,100]),
createSquare(30,200, [-100,-100]),
];

const createRotation = angleInDegrees => {
let angleInRadians = (angleInDegrees * Math.PI) / 180;
return [Math.sin(angleInRadians), Math.cos(angleInRadians)];
};

let start = 180;

setInterval(() => {
let rotation = createRotation(start);
start += 1;

gl.clear(gl.COLOR_BUFFER_BIT);

for (let i = 0; i < vtxData.length; i++) {
const arrVtx = new Float32Array(t(vtxData[i]));

const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, arrVtx, gl.STATIC_DRAW);
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);

gl.uniform2fv(a_Rotation, rotation);

const a_PointSize = gl.getAttribLocation(glProgram, 'a_PointSize');
gl.vertexAttrib1f(a_PointSize, 1.0);

const u_FragColor = gl.getUniformLocation(glProgram, 'u_FragColor');
gl.uniform4f(u_FragColor, 0.0, 0.6, 0.38, 1);

gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
}, 5);

旋转

只要修改index.vs,添加 uniform vec2 u_scale,然后与position相乘即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
attribute vec2 a_Position;
attribute float a_PointSize;
uniform vec2 a_Rotation;
uniform vec2 u_scale;

void main(){
vec2 scaledPosition = a_Position * u_scale;

vec2 rotatedPosition = vec2(
scaledPosition.x * a_Rotation.y + scaledPosition.y * a_Rotation.x,
scaledPosition.y * a_Rotation.y - scaledPosition.x * a_Rotation.x
);

gl_Position = vec4(rotatedPosition * vec2(1, -1), 0, 1);
gl_PointSize=a_PointSize;
}

javascript处获取并赋予变量值

1
2
3
4
5
6
7
8
9
// 获取变量
const a_Scale = gl.getUniformLocation(glProgram, 'u_scale');

// ...

let scale = [2,1];

// 赋予变量值
gl.uniform2fv(a_Scale, scale);

最终效果图

#

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×