【可视化学习】92-从入门到放弃WebGL(二十一)
发表于:2024-09-29 |

前言

上一篇文章中,我们已经成功封装了一个自己的webgl的轻量级框架,本篇文章使用这个框架来实现前面讲的彩色立方体和多着色器绘图的案例。

测试1-多着色器绘图

我们可以基于之前多着色器的例子,测试一下webgl框架在多着色器中的应用

俩套着色器

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
<!-- 着纯色 -->
<script id="solidVertexShader" type="x-shader/x-vertex">
attribute vec4 a_Position;
uniform mat4 u_PvMatrix;
uniform mat4 u_ModelMatrix;
void main(){
gl_Position = u_PvMatrix*u_ModelMatrix*a_Position;
}
</script>
<script id="solidFragmentShader" type="x-shader/x-fragment">
precision mediump float;
uniform float u_Time;
void main(){
float r=(sin(u_Time/200.0)+1.0)/2.0;
gl_FragColor=vec4(r,0.7,0.4,1);
}
</script>
<!-- 着纹理 -->
<script id="textureVertexShader" type="x-shader/x-vertex">
attribute vec4 a_Position;
attribute vec2 a_Pin;
uniform mat4 u_PvMatrix;
uniform mat4 u_ModelMatrix;
varying vec2 v_Pin;
void main(){
gl_Position = u_PvMatrix*u_ModelMatrix*a_Position;
v_Pin=a_Pin;
}
</script>
<script id="textureFragmentShader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_Sampler;
varying vec2 v_Pin;
void main(){
gl_FragColor=texture2D(u_Sampler,v_Pin);
}
</script>

引入js 模块

1
2
3
4
5
6
7
import { createProgram } from '../jsm/Utils.js';
import { Matrix4, OrthographicCamera, Vector3 } from 'https://unpkg.com/three/build/three.module.js';
import OrbitControls from './jsm/OrbitControls.js'
import Mat from './jsm/Mat.js'
import Geo from './jsm/Geo.js'
import Obj3D from './jsm/Obj3D.js'
import Scene from './jsm/Scene.js'

准备webgl上下文对象

1
2
3
4
5
const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const gl = canvas.getContext('webgl');
gl.clearColor(0.0, 0.0, 0.0, 1.0);

准备相机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const halfH = 1
const ratio = canvas.width / canvas.height
const halfW = halfH * ratio
const [left, right, top, bottom, near, far] = [
-halfW, halfW, halfH, -halfH, 1, 8
]
const eye = new Vector3(0, 0, 2)
const target = new Vector3(0, 0, 0)
const camera = new OrthographicCamera(
left, right, top, bottom, near, far
)
camera.position.copy(eye)
camera.lookAt(target)
camera.updateMatrixWorld()

实例化场景对象

1
const scene = new Scene({ gl })

建立纯色三角形

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
{
const vs = document.getElementById('solidVertexShader').innerText
const fs = document.getElementById('solidFragmentShader').innerText
const program = createProgram(gl, vs, fs)
const mat = new Mat({
program,
data: {
u_Time: {
value: 0,
type: 'uniform1f',
},
u_PvMatrix: {
value: new Matrix4().elements,
type: 'uniformMatrix4fv',
},
u_ModelMatrix: {
value: new Matrix4().elements,
type: 'uniformMatrix4fv',
},
}
})
const geo = new Geo({
data: {
a_Position: {
array: new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, -0.5,
]),
size: 2
}
}
})
const obj = new Obj3D({ geo, mat })
scene.add(obj)
}

建立纹理三角形

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
const image = new Image()
image.src = './images/erha.jpg'
image.onload = function () {
const vs = document.getElementById('textureVertexShader').innerText
const fs = document.getElementById('textureFragmentShader').innerText
const program = createProgram(gl, vs, fs)
const mat = new Mat({
program,
data: {
u_PvMatrix: {
value: new Matrix4().elements,
type: 'uniformMatrix4fv',
},
u_ModelMatrix: {
value: new Matrix4().elements,
type: 'uniformMatrix4fv',
},
},
maps: {
u_Sampler: {
image,
}
}
})
const geo = new Geo({
data: {
a_Position: {
array: new Float32Array([
0.5, 0.5,
-0.5, 0.5,
0.5, -0.5,
]),
size: 2
},
a_Pin: {
array: new Float32Array([
1, 1,
0, 1,
1, 0,
]),
size: 2
}
}
})
const obj = new Obj3D({ geo, mat })
scene.add(obj)

/* 统一设置uniform变量 */
scene.setUniform(
'u_PvMatrix',
{
value: camera.projectionMatrix.clone().multiply(
camera.matrixWorldInverse
).elements
}
)
}

渲染方法

1
2
3
4
5
6
render()
function render(time=0) {
scene.children[0].mat.setData('u_Time', { value: time })
scene.draw()
requestAnimationFrame(render)
}

测试2-彩色立方体

我们再拿之前的彩色立方体来测试一下webgl框架的顶点索引功能。

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_PvMatrix;
uniform mat4 u_ModelMatrix;
varying vec4 v_Color;
void main(){
gl_Position = u_PvMatrix*u_ModelMatrix*a_Position;
v_Color=a_Color;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 v_Color;
void main(){
gl_FragColor=v_Color;
}
</script>
<script type="module">
import { initShaders, createProgram } from '../jsm/Utils.js';
import { Matrix4, PerspectiveCamera, Vector3, Quaternion } from 'https://unpkg.com/three/build/three.module.js';
import OrbitControls from './jsm/OrbitControls.js'
import Mat from './jsm/Mat.js'
import Geo from './jsm/Geo.js'
import Obj3D from './jsm/Obj3D.js'
import Scene from './jsm/Scene.js'

const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const gl = canvas.getContext('webgl');

const vsSource = document.getElementById('vertexShader').innerText;
const fsSource = document.getElementById('fragmentShader').innerText;
const program = createProgram(gl, vsSource, fsSource);
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);



/* 透视相机 */
const eye = new Vector3(2, 3, 5)
const target = new Vector3(0, 0, 0)
const up = new Vector3(0, 1, 0)
const [fov, aspect, near, far] = [
45,
canvas.width / canvas.height,
1,
20
]
const camera = new PerspectiveCamera(fov, aspect, near, far)
camera.position.copy(eye)

/* 实例化轨道控制器 */
const orbit = new OrbitControls({
camera, target,
dom: canvas,
})
orbit.update()

/* 取消右击菜单的显示 */
canvas.addEventListener('contextmenu', event => {
event.preventDefault()
})
/* 指针按下时,设置拖拽起始位,获取轨道控制器状态。 */
canvas.addEventListener('pointerdown', event => {
orbit.pointerdown(event)
})
/* 指针移动时,若控制器处于平移状态,平移相机;若控制器处于旋转状态,旋转相机。 */
canvas.addEventListener('pointermove', event => {
orbit.pointermove(event)
})
/* 指针抬起 */
canvas.addEventListener('pointerup', event => {
orbit.pointerup(event)
})
/* 滚轮事件 */
canvas.addEventListener('wheel', event => {
orbit.wheel(event)
})


const scene = new Scene({ gl })
const mat = new Mat({
program,
data: {
u_Time: {
value: 0,
type: 'uniform1f',
},
u_PvMatrix: {
value: orbit.getPvMatrix().elements,
type: 'uniformMatrix4fv',
},
u_ModelMatrix: {
value: new Matrix4().elements,
type: 'uniformMatrix4fv',
},
}
})
const geo = new Geo({
data: {
a_Position: {
array: new Float32Array([
1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,
1, -1, -1,
1, 1, -1,
-1, 1, -1,
-1, -1, -1,
]),
size: 3
},
a_Color: {
array: new Float32Array([
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 1, 0,
0, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0
]),
size: 3
},
},
index: {
array: new Uint8Array([
0, 1, 2, 0, 2, 3, // front
0, 3, 4, 0, 4, 5, // right
0, 5, 6, 0, 6, 1, // up
1, 6, 7, 1, 7, 2, // left
7, 4, 3, 7, 3, 2, // down
4, 7, 6, 4, 6, 5 // back
])
}
})
const obj = new Obj3D({ geo, mat })
scene.add(obj)

!(function ani() {
scene.setUniform(
'u_PvMatrix',
orbit.getPvMatrix().elements
)
scene.draw()
requestAnimationFrame(ani)
})()

</script>

效果图

结语

本篇文章就到这里,更多内容敬请期待,债见~

上一篇:
【可视化学习】93-从入门到放弃WebGL(二十二)
下一篇:
【可视化学习】91-从入门到放弃WebGL(二十)