携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
回顾了一下昨天写的文章 vue写three.js例子-控制-物品拖动,代码编写过程写的过于简略了,一些解释和想法也没有表达出来(理科生真的文笔有限呀!所以今天的例子打算再写的详细一点。
今天要分享的是用vue写three.js例子-飞行漫游。先说一下大致的思路:先绘制一个场景,包括地球,地球云层,月球,和星星,然后通过飞行控制器,我们可以模拟自己在太空中飞行。以下是演示gif(有点大:从地球飞行到月球再飞回来,降临到表面的时候速度会变慢
依旧省略three.js的安装、引用过程,因为第一篇文章已经说过了。
昨天的文章少了一个步骤,在这里补一下:
贴图文件的加载
上一篇文章引入了一个木纹的贴图文件,其实是three.js官方例子中的文件(作为一个前端咱也不会设计,只能用用现成的,就是下面的的textures文件,放到自己vue项目下的public目录下即可,建议models和js文件也一起拷贝过来,后面的例子可能会用到
下面贴上我的代码(并附上我认为比较详细的讲解。
同样是先写一个容器
<div class="item">
<div id="THREE12"></div>
</div>
然后是mounted方法
因为下面要创建地球模型、地球云层模型、月球模型、星星模型,所以一开始定义了一堆变量来存,我先一一说明一下:
radius:地球的半径
rotationSpeed:地球自转的速度
cloudsScale:云层球体相较于地球球体的缩放比例
moonScale:月球球体相较于地球球体的缩放比例
geometry:存球体的变量
meshPlanet:存地球网格模型的变量
meshClouds:存云层网格模型的变量
meshMoon:存月球网格模型的变量
dirLight:存平行光的变量
d, dPlanet, dMoon:用来算速度,作出靠近球体表面减速的效果
dMoonVec:一个三维坐标
new THREE.Clock():相当于Date().now()
mounted() {
this.initThreejs();
},
methods: {
initThreejs() {
const radius = 6371;
const rotationSpeed = 0.02;
const cloudsScale = 1.005;
const moonScale = 0.23;
let SCREEN_HEIGHT = window.innerHeight;
let SCREEN_WIDTH = window.innerWidth - 201;
let camera, controls, scene, renderer;
let geometry, meshPlanet, meshClouds, meshMoon;
let dirLight;
const textureLoader = new THREE.TextureLoader();
let d, dPlanet, dMoon;
const dMoonVec = new THREE.Vector3();
const clock = new THREE.Clock();
init();
animate();
}
}
下面是init()的代码:
创建相机
new THREE.PerspectiveCamera():使用该方法创建一个透视相机,相机的垂直锥面的角度是25,相机视图锥体的长宽比是SCREEN_WIDTH / SCREEN_HEIGHT,相机视图锥体近端面的距离是50,相机视图锥体远端面的距离是1千万
1e7:1后面7个0,10000000,1千万
camera.position.z:相机的z轴位置
camera = new THREE.PerspectiveCamera(
25,
SCREEN_WIDTH / SCREEN_HEIGHT,
50,
1e7
);
camera.position.z = radius * 10;
创建场景
new THREE.Scene():使用该方法创建一个场景
scene = new THREE.Scene();
创建灯光
new THREE.DirectionalLight(0xffffff):使用该方法创建平行光,光的颜色为0xffffff,光照强度缺省,默认为1
dirLight.position.set(-1, 0, 1).normalize():设置光线的照射方向,我理解为从(0,0,0)-(-1,0,1)的方向
dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(-1, 0, 1).normalize();
scene.add(dirLight);
创建地球
new THREE.MeshPhongMaterial():一种用于具有镜面高光的光泽表面的材质
- specular:用于设置材质的高光颜色
- shininess:用于设置高亮的程度,越高的值越闪亮
- map:颜色贴图,这里贴了一张世界地图
new THREE.SphereGeometry(radius, 100, 50):创建一个半径为radius的球体
new THREE.Mesh(geometry, materialNormalMap):创建一个形状为球体,材质为上面定义的镜面高光材质的网格模型
const materialNormalMap = new THREE.MeshPhongMaterial({
specular: 0x333333,
shininess: 15,
map: textureLoader.load("textures/planets/earth_atmos_2048.jpg"),
});
geometry = new THREE.SphereGeometry(radius, 100, 50);
meshPlanet = new THREE.Mesh(geometry, materialNormalMap);
scene.add(meshPlanet);
创建云层
new THREE.MeshLambertMaterial():一种非光泽表面的材质,没有镜面高光
- map:颜色贴图,这里贴了云
new THREE.Mesh(geometry, materialClouds):创建一个形状为球体,材质为云的网格模型
meshClouds.scale.set(cloudsScale, cloudsScale, cloudsScale):云层缩放cloudsScale倍数
const materialClouds = new THREE.MeshLambertMaterial({
map: textureLoader.load("textures/planets/earth_clouds_1024.png"),
});
meshClouds = new THREE.Mesh(geometry, materialClouds);
meshClouds.scale.set(cloudsScale, cloudsScale, cloudsScale);
scene.add(meshClouds);
创建月球
这里贴了月亮表面的材质,创建了一个形状为球体,材质为月亮的网格模型,并且进行了moonScale倍的缩放,位置在离地球radius * 5距离的地方。
const materialMoon = new THREE.MeshPhongMaterial({
map: textureLoader.load("textures/planets/moon_1024.jpg"),
});
meshMoon = new THREE.Mesh(geometry, materialMoon);
meshMoon.position.set(radius * 5, 0, 0);
meshMoon.scale.set(moonScale, moonScale, moonScale);
scene.add(meshMoon);
渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
document.getElementById("THREE12").appendChild(renderer.domElement);
飞行控制器
new FlyControls():创建一个飞行控制器
- movementSpeed:移动速度,默认为1
- rollSpeed:旋转速度,默认为0.005
- autoForward:若该值设为true,初始变换后,相机将自动向前移动(且不会停止)。默认为false。
- dragToLook:若该值设为true,你将只能通过执行拖拽交互来环视四周。默认为false。
以上为官方说明,暂无更多理解
controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = Math.PI / 32;
controls.autoForward = false;
controls.dragToLook = false;
渲染方法
dMoonVec.subVectors(camera.position, meshMoon.position); // 将向量dMoonVec设置为camera.position - meshMoon.position
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
// 地球和云层自转
const delta = clock.getDelta();
meshPlanet.rotation.y += rotationSpeed * delta;
meshClouds.rotation.y += 1.25 * rotationSpeed * delta;
// 缓慢降落至表面
dPlanet = camera.position.length();
dMoonVec.subVectors(camera.position, meshMoon.position);
dMoon = dMoonVec.length();
if (dMoon < dPlanet) {
d = dMoon - radius * moonScale * 1.01;
} else {
d = dPlanet - radius * 1.01;
}
controls.movementSpeed = 0.33 * d;
controls.update(delta);
renderer.render(scene, camera);
}