封面图
这个实现过程比较简单,主要用来练习一下ThreeJS中的基本操作,场景、灯光、相机、几何体。
实现过程
要是实现这个demo ,我们首先必须要先创建一个基本的场景,先把舞台搭建好,这些都是正常的流程,比如我这里用的vue3起的项目,那么代码如下:
<template>
<div ref="world" class="gemo-wrapper">
</div>
</template>
<script setup>
import { ref, unref, onMounted, watch } from 'vue'
import { Input, InputNumber } from 'ant-design-vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'
import { CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
import * as dat from 'dat.gui'
import gsap from 'gsap'
let scene
let camera
let fov
let asp
let near
let far
let height
let width
let renderer
const world = ref();
const colors = {
red: 0xf25346,
white: 0xd8d0d1,
brown: 0x59332e,
pink: 0xF5986E,
brownDark: 0x23190f,
blue: 0x68c3c0,
}
const init = () => {
createScene()
createLights()
createPlane()
// loop
loop()
var controls = new OrbitControls(camera, renderer.domElement);
controls.enablePan = false;
}
onMounted(() => {
init()
})
</script>
然后我们需要把createScene
以及createLights
补全,因为是demo,所以大家不要在意变量的定义规范,自己喜欢就好,代码如下:
const createScene = () => {
height = world.value.clientHeight
width = world.value.clientWidth
scene = new THREE.Scene()
asp = width / height
fov = 60
near = 1
far = 10000
camera = new THREE.PerspectiveCamera(
fov,
asp,
near,
far
);
// camera.lookAt(0, 0, 0)
camera.position.x = 0;
camera.position.z = 200;
camera.position.y = 100;
renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
renderer.setSize(width, height)
renderer.shadowMap.enabled = true
world.value.appendChild(renderer.domElement)
}
// 灯光
let hemisphereLight, shadowLight
const createLights = () => {
// 用一个半球光来展示渐变的云朵
// 第一个参数是天空的颜色,第二个参数是地面颜色,第三个参数是光线强度
hemisphereLight = new THREE.HemisphereLight(0xaaaaaa, 0x000000, .9)
// 从指定位置放置一个平行光,代表太阳
shadowLight = new THREE.DirectionalLight(0xffffff, .9)
// 设置光线的方向
shadowLight.position.set(150, 350, 350)
// 允许投影
shadowLight.castShadow = true
// 设置允许投影的范围
shadowLight.shadow.camera.left = -400
shadowLight.shadow.camera.right = 400
shadowLight.shadow.camera.top = 400
shadowLight.shadow.camera.bottom = -400
shadowLight.shadow.camera.near = 1
shadowLight.shadow.camera.far = 1000
// 定义阴影的分辨率,越高效果越好,但是相对就消耗性能
shadowLight.shadow.mapSize.width = 2048
shadowLight.shadow.mapSize.height = 2048
// 将他们添加到场景中
scene.add(hemisphereLight)
scene.add(shadowLight)
}
关于灯光,hemisphereLight是半球光,这个光非常有意思,一部分是从下往上的平行光,一部分是从上半球往中心点的光。如图:
它的第一个参数是天空的颜色,第二个参数是地面颜色,第三个参数是光线强度。
设置场景和灯光之后,我们就要开始思考怎么绘制这个小飞机了,其实也很简单,就是一推方块儿,将他们设置成合适的大小,放在合适的位置,就行,如图:
小飞机可以拆分成,机舱、发动机、机翼、机尾、螺旋桨这几个部分,那么我们就可以按照这个逻辑分别用几何体将这部分绘制出来,然后组装到一起即可。
绘制飞机
我这里定义了一个Plane类。
export class Plane {
mesh;
geomCockpit;
matCockpit;
cockpit; // 机舱
engine; // 引擎
tail; // 机尾巴
wing; // 鸡翅膀
propeller; // 螺旋桨
blade; // 螺旋桨叶片
constructor() {
this.mesh = new THREE.Object3D();
this.geomCockpit = new THREE.BoxGeometry(60, 50, 50, 1, 1, 1);
this.matCockpit = new THREE.MeshPhongMaterial({
color: colors.red,
shading: THREE.FlatShading,
});
this.cockpit = new THREE.Mesh(this.geomCockpit, this.matCockpit);
this.mesh.add(this.cockpit);
this.init();
}
init() {
this.createEngine();
this.createTail();
this.createWing();
this.createPropeller();
this.createBlades();
}
}
构造器中对机舱做了个初始化,然后执行了init
方法,init
方法中又分别创建了机舱、发动机、机翼、机尾、螺旋桨,我们只需要挨个实现即可。
绘制引擎
createEngine() {
let geomEngine = new THREE.BoxGeometry(20, 50, 50, 1, 1, 1);
let matEngine = new THREE.MeshPhongMaterial({
color: colors.red,
shading: THREE.FlatShading,
});
this.engine = new THREE.Mesh(geomEngine, matEngine);
this.engine.position.x = 40;
this.engine.castShadow = true;
this.engine.receiveShadow = true;
console.log("engine", this.engine);
this.mesh.add(this.engine);
}
绘制尾巴
createTail() {
let geomTail = new THREE.BoxGeometry(15, 20, 5, 1, 1, 1);
let matTail = new THREE.MeshPhongMaterial({
color: colors.red,
shading: THREE.FlatShading,
});
this.tail = new THREE.Mesh(geomTail, matTail);
this.tail.position.set(-35, 25, 0);
this.tail.castShadow = true;
this.tail.receiveShadow = true;
this.mesh.add(this.tail);
}
绘制鸡翅膀
createWing() {
let geomWing = new THREE.BoxGeometry(40, 8, 150, 1, 1, 1);
let matWing = new THREE.MeshPhongMaterial({
color: colors.red,
shading: THREE.FlatShading,
});
this.wing = new THREE.Mesh(geomWing, matWing);
this.wing.castShadow = true;
this.wing.receiveShadow = true;
this.mesh.add(this.wing);
}
绘制螺旋桨和叶片
createPropeller() {
let geomPropeller = new THREE.BoxGeometry(20, 10, 10, 1, 1, 1);
let matPropeller = new THREE.MeshPhongMaterial({
color: colors.brownDark,
shading: THREE.FlatShading,
});
this.propeller = new THREE.Mesh(geomPropeller, matPropeller);
this.propeller.castShadow = true;
this.propeller.receiveShadow = true;
}
createBlades() {
let geomBlades = new THREE.BoxGeometry(1, 100, 20, 1, 1, 1);
let matBlades = new THREE.MeshPhongMaterial({
color: colors.brownDark,
shading: THREE.FlatShading,
});
this.blade = new THREE.Mesh(geomBlades, matBlades);
this.blade.position.set(8, 0, 0);
this.blade.castShadow = true;
this.blade.receiveShadow = true;
this.propeller.add(this.blade);
this.propeller.position.set(50, 0, 0);
this.mesh.add(this.propeller);
}
可以看到,这里没什么比较特别需要注意的地方,都是一些简单的创建几何体,然后设置位置,开启投影。
如果非要有一点需要注意的话,可以看下THREE.FlatShading
相关的资料,它是threejs中默认的一个着色器,计算三角形质心并根据该向量填充所有颜色来完成。
向场景中添加飞机
完成Plane
类之后,我们就可以向场景中添加飞机了,很简单:
let plane
const createPlane = () => {
plane = new Plane()
console.log('plane', plane)
plane.mesh.scale.set(.25, .25, .25)
plane.mesh.position.y = 50
scene.add(plane.mesh)
}
然后加个动画跟轨道控制:
var controls = new OrbitControls(camera, renderer.domElement);
controls.enablePan = false;
const loop = () => {
plane.propeller.rotation.x += 0.3;
sky.rotation.z += .01;
renderer.render(scene, camera)
requestAnimationFrame(loop)
}
就可以实现下面的效果了: