先上效果图:
- 首先需要安装并引入three:
npm install three
import * as THREE from "three";
- 创建并初始化three的三大要素(scene,camera,render)
//添加场景scene
this.scene = new THREE.Scene()
//添加摄像头camera
let container = document.getElementById("web");
this.camera = new THREE.PerspectiveCamera(70, container.clientWidth / container.clientHeight, 0.01, 200);
this.camera.position.set(10,5,10)
//添加场景渲染器render
this.renderer = new THREE.WebGLRenderer({ antialias: true});
this.renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild(this.renderer.domElement);
//添加标签渲染器CSS2DRenderer(应用于CSS2DObject)
this.labelRender = new CSS2DRenderer()
this.labelRender.setSize(container.clientWidth, container.clientHeight);
this.labelRender.domElement.style.position = 'absolute'
this.labelRender.domElement.style.top = '0px'
container.appendChild(this.labelRender.domElement);
- 创建mesh对象,包含geometry和material,并添加至场景内
//创建地球
let earthGeometry = new THREE.SphereGeometry(5,200,200)
let earthMaterial = new THREE.MeshPhongMaterial({
shininess:5,
map: new THREE.TextureLoader().load(require('./assets/Earth.png')),
specularMap: new THREE.TextureLoader().load(require('./assets/EarthSpec.png')),
normalMap: new THREE.TextureLoader().load(require('./assets/EarthNormal.png'))
})
this.earthMesh = new THREE.Mesh(earthGeometry,earthMaterial)
this.scene.add(this.earthMesh)//添加到场景内
//创建月球
let moonGeometry = new THREE.SphereGeometry(0.27,20,20)
let moonMaterial = new THREE.MeshPhongMaterial({
shininess:5,
map: new THREE.TextureLoader().load(require('./assets/moon.jpg'))
})
this.moonMesh = new THREE.Mesh(moonGeometry,moonMaterial)
this.scene.add(this.moonMesh)//添加到场景内
- 创建并初始化标记label,并添加至场景内
//创建地球标签
let earthDiv = document.createElement('div')
earthDiv.className = 'label'
earthDiv.textContent = '地球'
let earthLabel = new CSS2DObject(earthDiv)
earthLabel.position.set(0,6,0)
this.earthMesh.add(earthLabel)
this.scene.add(this.earthMesh)
//创建月球标签
let moonDiv = document.createElement('div')
moonDiv.className = 'label'
moonDiv.textContent = '月球'
let moonLabel = new CSS2DObject(moonDiv)
moonLabel.position.set(0,0.5,0)
this.moonMesh.add(moonLabel)
this.scene.add(this.moonMesh)
- 创建并初始化灯光,并添加至场景内
//添加点光
let spotLight = new THREE.SpotLight('white')
spotLight.position.set( 0, 0, 10 );
spotLight.intensity = 2;
spotLight.castShadow = true;
this.scene.add(spotLight)
//添加环境光
let aLight = new THREE.AmbientLight(0x404040,3)
this.scene.add(aLight)
- 创建并初始化控件对象
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
let control = new OrbitControls(this.camera, this.labelRender.domElement);
- 添加动画效果并开始渲染
animate(){
let duration = this.clock.getElapsedTime()
//公转
this.moonMesh.position.set(Math.sin(duration)*8,0,Math.cos(duration)*8)
//自转
this.moonMesh.rotation.y += 0.01
this.earthMesh.rotation.y += 0.005
this.renderer.render(this.scene, this.camera);
this.labelRender.render(this.scene, this.camera)
requestAnimationFrame(this.animate);
}
- 解决显示分辨率变化的问题,需要重置camera和render的长宽比
window.onresize = ()=>{
let container = document.getElementById("web");
this.camera.aspect = container.clientWidth / container.clientHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(container.clientWidth, container.clientHeight)
this.labelRender.setSize(container.clientWidth, container.clientHeight)
}
全部代码如下,需要的尽管拿去
<template>
<div>
<div id="web"></div>
</div>
</template>
<script>
import * as THREE from "three";
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import {CSS2DRenderer, CSS2DObject} from 'three/examples/jsm/renderers/CSS2DRenderer'
export default {
name:'earth',
data() {
return {
camera: null,
scene: null,
renderer: null,
earthMesh: null,
moonMesh: null,
controls:null,
clock: new THREE.Clock(),
labelRender: null,
group: new THREE.Group()
}
},
mounted(){
this.init()
this.animate()
window.onresize = ()=>{
let container = document.getElementById("web");
this.camera.aspect = container.clientWidth / container.clientHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(container.clientWidth, container.clientHeight)
this.labelRender.setSize(container.clientWidth, container.clientHeight)
}
},
methods: {
init(){
//添加场景
this.scene = new THREE.Scene()
//添加地球
let earthGeometry = new THREE.SphereGeometry(5,200,200)
let earthMaterial = new THREE.MeshPhongMaterial({
shininess:5,
map: new THREE.TextureLoader().load(require('./assets/Earth.png')),
specularMap: new THREE.TextureLoader().load(require('./assets/EarthSpec.png')),
normalMap: new THREE.TextureLoader().load(require('./assets/EarthNormal.png'))
})
this.earthMesh = new THREE.Mesh(earthGeometry,earthMaterial)
this.earthMesh.castShadow = true
this.earthMesh.receiveShadow = true
let earthDiv = document.createElement('div')
earthDiv.className = 'label'
earthDiv.textContent = '地球'
let earthLabel = new CSS2DObject(earthDiv)
earthLabel.position.set(0,6,0)
this.earthMesh.add(earthLabel)
this.scene.add(this.earthMesh)
//添加月球
let moonGeometry = new THREE.SphereGeometry(0.27,20,20)
let moonMaterial = new THREE.MeshPhongMaterial({
shininess:5,
map: new THREE.TextureLoader().load(require('./assets/moon.jpg'))
})
this.moonMesh = new THREE.Mesh(moonGeometry,moonMaterial)
this.moonMesh.castShadow = true
this.moonMesh.receiveShadow = true
let moonDiv = document.createElement('div')
moonDiv.className = 'label'
moonDiv.textContent = '月球'
let moonLabel = new CSS2DObject(moonDiv)
moonLabel.position.set(0,0.5,0)
this.moonMesh.add(moonLabel)
this.scene.add(this.moonMesh)
//添加点光
let spotLight = new THREE.SpotLight('white')
spotLight.position.set( 0, 0, 10 );
spotLight.intensity = 2;
spotLight.castShadow = true;
this.scene.add(spotLight)
//添加环境光
let aLight = new THREE.AmbientLight(0x404040,3)
this.scene.add(aLight)
//添加摄像头
let container = document.getElementById("web");
this.camera = new THREE.PerspectiveCamera(70, container.clientWidth / container.clientHeight, 0.01, 200);
this.camera.position.set(10,5,10)
//添加渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true});
this.renderer.setSize(container.clientWidth, container.clientHeight);
this.renderer.shadowMap.enabled = true
container.appendChild(this.renderer.domElement);
this.labelRender = new CSS2DRenderer()
this.labelRender.setSize(container.clientWidth, container.clientHeight);
this.labelRender.domElement.style.position = 'absolute'
this.labelRender.domElement.style.top = '0px'
container.appendChild(this.labelRender.domElement);
//创建控件对象
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
let control = new OrbitControls(this.camera, this.labelRender.domElement);
control.enableDamping = true
},
animate(){
let duration = this.clock.getElapsedTime()
//公转
this.moonMesh.position.set(Math.sin(duration)*8,0,Math.cos(duration)*8)
//自转
this.moonMesh.rotation.y += 0.01
this.earthMesh.rotation.y += 0.005
this.renderer.render(this.scene, this.camera);
this.labelRender.render(this.scene, this.camera)
requestAnimationFrame(this.animate);
}
}
}
</script>
<style>
#web {
position: absolute;
width: 100%;
height: 100%;
}
.label {
color: white;
font-size: 16px;
margin-bottom: 5px;
}
</style>
Earth.png
EarthNormal.png
EarthSpec.png
moon.jpg