前言
在上一章中我,我们利用一些基本的类创建了一个简单的程序,并且创建了一个正立方体(虽然看起来像正方形),但是如果你适当的调整相机的位置,是可以让正方形
看起来更加像的立体的,有兴趣可以去尝试一下
下面我要讲的是程序的模块化:模块化是一种的软件设计技术,旨在将一个大的程序分解成若干个独立的模块module
,每个模块完成一项具体的子功能,它可以提高的代码的可读性
、复用性
、可维护性
、可测试性
、协同开发
,所以开发中使用是很有必要的
我们开发的3D程序一般是嵌套在页面中的,代码肯定也是要和页面其他的功能点相隔离(不分离出来会让代码变得很混乱,并且难以维护),所以我们要为3D程序代码单独创建创建一个模块(组件),为的就是后期的复用
和方便维护
,在使用到的页面直接引用就可以了,提高开发效率
创建应用
书中是用 世界
来进行举例的;我们开发的内容都存在于这个世界
之中,所以文件夹以 World
命名 创建的基本步骤:
- 初始设置
- 创建场景
- 创建相机
- 创建立方体并将其添加到场景中
- 创建渲染器
- 渲染场景
初始设置 初始化World 类
首先创建一个 World
文件夹,我们的代码全部存储在这里,在同一层级下创建一个World.js
文件,这个文件会将我们所有的功能进行整合,不同模块的参数传递也是通过这个文件进行
js
// src/World.js
class World {
constructor(container) {}
render(){}
}
// 导出这个类,方便在main.js中使用
export {World}
js
// main.js
import { World } from './src/World.js';
const main = () => {
const container = document.querySelector('#app')
const world = new World(container)
world.render()
}
main()
创建场景
后面一般很少会修改这个文件
书中没有介绍辅助线
,我觉得辅助线
还是蛮好用的,后面结合OrbitControls
使得相机围绕目标进行轨道运动,可以帮助我们更快定位到我们当前所处的位置
注:辅助线红色代表X轴、绿色代表Y轴、蓝色代表Z轴
js
// src/World/components/scene.js
import { Color, Scene, AxesHelper } from 'three'
const createScene = () => {
const scene = new Scene()
scene.background = new Color('#bedebc') // 画布背景
// 创建辅助线添加到场景
const axesHelper = new AxesHelper(50) // 设置辅助线长度
scene.add(axesHelper)
return scene
}
export {createScene}
创建相机
js
// src/World/components/camera.js
import { PerspectiveCamera } from 'three'
const createCamera = () => {
const fov = 35
const aspect = 1
const near = 0.1
const far = 1000
const camera = new PerspectiveCamera(fov, aspect, near, far)
camera.position.set(0,0,20)
return camera
}
export {createCamera}
创建立方体
js
// src/World/components/cube.js
import { BoxGeometry, Mesh, MeshBasicMaterial } from 'three'
const createCube = () => {
const geometry = new BoxGeometry(2,2,2)
const material = new MeshBasicMaterial({color: 'purple'}) // 紫色
const cube = new Mesh(geometry, material)
return cube
}
export {createCube}
设置系统模块
js
// src/World/systems/renderer.js
import { WebGLRenderer } from 'three'
const createRenderer = () => {
const renderer = new WebGLRenderer()
return renderer
}
export {createRenderer}
js
// src/World/systems/Resizer.js
class Resizer {
constructor(container, camera, renderer) {
camera.aspect = container.clientWidth / container.clientHeight;
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
}
}
export {Resizer}
设置World类
首先我们将每个模块都在World.js
中导入 我们将设置相机
、场景
和渲染器
,他们都需要在构造函数中创建,然后在World.render
方法中访问, 同时保护好这些组件不要暴露给外界
js
// src/World.js
import { createCamera } form './World/components/camera'
import { createScene } form './World/components/scene'
import { createCube } form './World/components/cube'
import { createRenderer } form './World/systems/renderer'
import { Resizer } form './World/systems/Resizer'
class World {
#camera;
#scene;
#renderer;
constructor(container) {
this.#camera = createCamera();
this.#scene = createScene();
this.#renderer = createRenderer();
const cube = createCube()
this.#scene.add(cube)
// 将画布添加到容器中
container.append(this.#renderer.domElement)
const resizer = new Resizer(container, this.#camera, this.#renderer)
}
// 渲染场景
render() {
this.#renderer.render(this.#scene, this.#camera)
}
}
按照上面的操作,我们可以得到一个紫色的立方体,下面我们可以通过添加OrbitControls
轨道控制器,使用鼠标实现相机位置的变化
js
// src/World/systems/controls.js
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
const createControls = (camera, canvas) => {
const controls = new OrbitControls(camera, canvas);
return controls
}
export { createControls }
// src/World.js
})
js
// src/World.js
import { createControls } from "./World/systems/controls";
constructor(container) {
// 在 constructor 中使用轨道控制器
this.controls = createControls(this.#camera, this.#renderer.domElement)
// 每次发生变化时都要重新渲染页面
this.controls.addEventListener('change', () => {
this.render()
}
)
这些操作都做好之后,我们会得到一个下面这样的场景,使用鼠标可以旋转这个立方体,并且可以看到空间的辅助线
我们这里看到的立方体或许跟想象的不太一样,是因为我们使用的材质是不需要光照的,后面学习了光照之后,我们会使用MeshStandardMaterial
来创建模型的材质,不同材质在不同的灯光下的表现也是不一样的,后面再慢慢探索
轨道控制器
最后在介绍下轨道控制器:他可以让相机围绕目标
进行运动,既然是围绕目标运动,那就必然有一个运动中心,默认的运动中心是(0,0,0),
后面我们可以根据自己的需要进行修改(尤其是多个模型的时候,会经常用到运动中心的修改),下面就说一些轨道控制器
常用的方法和属性
js
const controls = new OrbitControls(camera, canvas);
// 更改旋转中心的位置,默认(0,0,0)
controls.target.set(10,10,10)
// 增加阻尼(提高旋转操作时的体验,需要调用update())
controls.enableDamping = true
controls.dampingFactor = 0.1
// 自动旋转(需要调用update())
controls.autoRotate = true
controls.autoRotateSpeed = 1
// 移除所有的事件监听,销毁控件controls,
constrols.dispose()
目前我用到的主要是这些属性,部分属性直接使用是不会生效的,需要在动画循环中调用.update()
,这点官网是有介绍的,后面到动画那个篇章会具体的介绍
总结
这一期主要是将各个功能点给分离开来,每一个都可以作为独立的模块,方便后面继续往不同的模块添加新的功能,而不至于让代码变得难以维护。后面的内容也是基于这个结构进行开发的
如果文章中有描述不准确或错误的地方,欢迎各位大佬指正😀😀😀