ThreeJs结合Vue实现月球绕地

359 阅读1分钟

先上效果图:

earthMoon.gif

  1. 首先需要安装并引入three:
npm install three
import * as THREE from "three";
  1. 创建并初始化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);
  1. 创建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)//添加到场景内
  1. 创建并初始化标记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)
  1. 创建并初始化灯光,并添加至场景内
//添加点光
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)
  1. 创建并初始化控件对象
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
let control = new OrbitControls(this.camera, this.labelRender.domElement);
  1. 添加动画效果并开始渲染
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);
}
  1. 解决显示分辨率变化的问题,需要重置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 Earth.png

EarthNormal.png EarthNormal.png

EarthSpec.png EarthSpec.png

moon.jpg

moon.jpg