【可视化学习】22-Three.js物体形变
发表于:2023-06-11 |

基础代码

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
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 导入动画库
import gsap from "gsap";
// 导入dat.gui
import * as dat from "dat.gui";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

const gui = new dat.GUI();
// 1、创建场景
const scene = new THREE.Scene();

// 2、创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 设置相机位置
camera.position.set(0, 0, 20);
scene.add(camera);



// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;

// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);

// // 使用渲染器,通过相机将场景渲染进来
// renderer.render(scene, camera);

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.enableDamping = true;

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 设置时钟
const clock = new THREE.Clock();
function render() {
let time = clock.getDelta();
controls.update();
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render);
}

render();

// 监听画面变化,更新渲染画面
window.addEventListener("resize", () => {
// console.log("画面变化了");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
});

效果图

添加环境纹理

1
2
3
4
5
6
7
// 添加hdr环境纹理
const loader = new RGBELoader();
loader.load("./textures/038.hdr", function (texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
scene.environment = texture;
});

效果图

配置render渲染器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;
// 物理校正灯
renderer.physicallyCorrectLights = true;
// 修改背景色
renderer.setClearColor(0xcccccc, 1);
// 对应帧缓冲区中的像素相关数据都会被覆盖
renderer.autoClear = false;
// 设置电影渲染模式
renderer.toneMapping = THREE.ACESFilmicToneMapping;
// 让材质可以接受光照
renderer.outputEncoding = THREE.sRGBEncoding;
// 开启接受设置渲染顺序
renderer.sortObjects = true;
// logarithmicDepthBuffer对数深度缓冲区,Three.js_解决谍影锯齿闪烁重影模型的方法
renderer.logarithmicDepthBuffer = true;

效果图

加载压缩的glb模型

1
2
3
4
5
6
7
8
9
10
11
const gltfLoader = new GLTFLoader();
// 加载压缩的glb模型需要使用这个loader
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("./draco/gltf/");
dracoLoader.setDecoderConfig({ type: "js" });
dracoLoader.preload();
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load("./model/f4.glb", function (gltf) {
console.log(gltf);
scene.add(gltf.scene);
})

效果图

旋转模型

1
gltf.scene.rotation.x = Math.PI;

效果图

添加水

我们可以看到,模型中的水没有加载进来,这是因为水材质映射了环境贴图,我们把水的材质修改成为基础材质就可以了
效果图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gltfLoader.load("./model/f4.glb", function (gltf) {
gltf.scene.rotation.x = Math.PI;
gltf.scene.traverse((item) => {
if (item.material && item.material.name == "Water") {
console.log(item);
item.material = new THREE.MeshStandardMaterial({
color: "skyblue",
depthWrite: false,
depthTest: false,
transparent: true,
opacity: 0.7,
});
}
})
scene.add(gltf.scene);
})

效果图

添加变形动画

变形实现逻辑,在第一个状态的geometry,缓冲区添加morphAttributes的变化数组数据,然后使用.updateMorphTargets()更新数据,morphTargetInfluences可以通过gsap实现动态变化,即可实现动画。

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
let params = {
value0: 0,
};
let stem, petal, stem1, petal1, stem2, petal2;
gltfLoader.load("./model/f4.glb", function (gltf) {
gltf.scene.rotation.x = Math.PI;
gltf.scene.traverse((item) => {
// 水
if (item.material && item.material.name == "Water") {
console.log(item);
item.material = new THREE.MeshStandardMaterial({
color: "skyblue",
depthWrite: false,
depthTest: false,
transparent: true,
opacity: 0.7,
});
}
// 茎
if (item.material && item.material.name == "Stem") {
stem = item;
}
// 花瓣
if (item.material && item.material.name == "Petal") {
petal = item;
}
gltfLoader.load("./model/f2.glb", (gltf) => {
gltf.scene.traverse((item) => {
if (item.material && item.material.name == "Petal") {
petal1 = item;
if (!petal.geometry.morphAttributes.position) {
petal.geometry.morphAttributes.position = [];
}
petal.geometry.morphAttributes.position[0] =
petal1.geometry.attributes.position;

petal.updateMorphTargets();
petal.morphTargetInfluences[0] = 1;

gsap.to(params, {
value0: 1,
duration: 10,
// repeat: -1,
delay: 0,
onUpdate: function () {
petal.morphTargetInfluences[0] = params.value0;
stem.morphTargetInfluences[0] = params.value0;
},
});
}
if (item.material && item.material.name == "Stem") {
stem1 = item;
if (!stem.geometry.morphAttributes.position) {
stem.geometry.morphAttributes.position = [];
}
stem.geometry.morphAttributes.position[0] =
stem1.geometry.attributes.position;
stem.updateMorphTargets();
stem.morphTargetInfluences[0] = 1;
}
});
})

})
scene.add(gltf.scene);
})

效果图

补充最后一部分动画之后的完整代码

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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 导入动画库
import gsap from "gsap";
// 导入dat.gui
import * as dat from "dat.gui";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

// 目标:点光源

const gui = new dat.GUI();
// 1、创建场景
const scene = new THREE.Scene();

// 2、创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 设置相机位置
camera.position.set(0, 0, 20);
scene.add(camera);

// 灯光
// 环境光
// const light = new THREE.AmbientLight(0xffffff, 1); // soft white light
// scene.add(light);

// 添加hdr环境纹理
const loader = new RGBELoader();
loader.load("./textures/038.hdr", function (texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
scene.environment = texture;
});

let params = {
value0: 0,
value1: 0,
};
// 加载压缩的glb模型
const gltfLoader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("./draco/gltf/");
dracoLoader.setDecoderConfig({ type: "js" });
dracoLoader.preload();
gltfLoader.setDRACOLoader(dracoLoader);
let mixer;
let stem, petal, stem1, petal1, stem2, petal2;
gltfLoader.load("./model/f4.glb", function (gltf) {
console.log(gltf);
gltf.scene.rotation.x = Math.PI;
gltf.scene.traverse((item) => {
if (item.material && item.material.name == "Water") {
console.log(item);
// item.renderOrder = 9;
// item.material.blending = THREE.AdditiveBlending;
// item.scale.set(0.8, 0.8, 0.8);
// item.material.depthTest = false;

// // item.material.transparent = false;
// item.material.depthWrite = true;
// item.material.side = THREE.DoubleSide;
// item.material = new THREE.MeshBasicMaterial({
// color: "skyblue",
// depthWrite: false,
// depthTest: false,
// transparent: true,
// opacity: 0.7,
// });

item.material = new THREE.MeshStandardMaterial({
color: "skyblue",
depthWrite: false,
depthTest: false,
transparent: true,
opacity: 0.7,
});
}
if (item.material && item.material.name == "Stem") {
stem = item;
}

if (item.material && item.material.name == "Petal") {
console.log(item);
petal = item;

gltfLoader.load("./model/f2.glb", (gltf) => {
gltf.scene.traverse((item) => {
if (item.material && item.material.name == "Petal") {
petal1 = item;
// console.log(petal1.geometry.attributes.position);
if (!petal.geometry.morphAttributes.position) {
petal.geometry.morphAttributes.position = [];
}
petal.geometry.morphAttributes.position[0] =
petal1.geometry.attributes.position;

console.log(petal.morphTargetInfluences);
petal.updateMorphTargets();
petal.morphTargetInfluences[0] = 1;
console.log(petal.geometry.morphAttributes);

gsap.to(params, {
value0: 1,
duration: 10,
// repeat: -1,
delay: 0,
onUpdate: function () {
petal.morphTargetInfluences[0] = params.value0;
stem.morphTargetInfluences[0] = params.value0;
},
});
}
if (item.material && item.material.name == "Stem") {
stem1 = item;
if (!stem.geometry.morphAttributes.position) {
stem.geometry.morphAttributes.position = [];
}
stem.geometry.morphAttributes.position[0] =
stem1.geometry.attributes.position;
stem.updateMorphTargets();
stem.morphTargetInfluences[0] = 1;
}
});

gltfLoader.load("./model/f1.glb", (gltf) => {
gltf.scene.traverse((item) => {
if (item.material && item.material.name == "Petal") {
petal2 = item;

// console.log(petal1.geometry.attributes.position);
petal.geometry.morphAttributes.position[1] =
petal2.geometry.attributes.position;
console.log(petal.morphTargetInfluences);
petal.updateMorphTargets();
petal.morphTargetInfluences[1] = 0;
console.log(petal.geometry.morphAttributes);

gsap.to(params, {
value1: 1,
duration: 10,
delay: 10,
// repeat: -1,
onUpdate: function () {
// console.log(petal.morphTargetInfluences);
console.log(stem.morphTargetInfluences);
petal.morphTargetInfluences[0] = params.value0;
stem.morphTargetInfluences[0] = params.value0;
petal.morphTargetInfluences[1] = params.value1;
stem.morphTargetInfluences[1] = params.value1;
},
});
}
if (item.material && item.material.name == "Stem") {
stem2 = item;

stem.geometry.morphAttributes.position[1] =
stem2.geometry.attributes.position;
stem.updateMorphTargets();
stem.morphTargetInfluences[1] = 0;
}
});
});
});
}
});
scene.add(gltf.scene);
});

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({
logarithmicDepthBuffer: true,
antialias: true,
});
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;
renderer.physicallyCorrectLights = true;
renderer.setClearColor(0xcccccc, 1);
renderer.autoClear = false;
// 设置电影渲染模式
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.sortObjects = true;
renderer.logarithmicDepthBuffer = true;

// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);

// // 使用渲染器,通过相机将场景渲染进来
// renderer.render(scene, camera);

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.enableDamping = true;

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 设置时钟
const clock = new THREE.Clock();
function render() {
let time = clock.getDelta();
if (mixer) {
// console.log(mixer);
mixer.update(time);
}
controls.update();

renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render);
}

render();

// 监听画面变化,更新渲染画面
window.addEventListener("resize", () => {
// console.log("画面变化了");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
});

效果图

上一篇:
uni-app如何启动
下一篇:
【可视化学习】21-CSS渲染器与曲线运动