目录
项目搭建
初始化three.js基础代码
创建月球模型
添加地球模型
添加模型标签
今天简单实现一个three.js的小Demo,加强自己对three知识的掌握与学习,只有在项目中才能灵活将所学知识运用起来,话不多说直接开始。
项目搭建
本案例还是借助框架书写three项目,借用vite构建工具搭建vue项目,vite这个构建工具如果有不了解的朋友,可以参考我之前对其讲解的文章:vite脚手架的搭建与使用 。搭建完成之后,用编辑器打开该项目,在终端执行 npm i 安装一下依赖,安装完成之后终端在安装 npm i three 即可。
<template>
<!-- 地月模型 -->
<EarthMoonSurrounding></EarthMoonSurrounding>
</template>
<script setup>
import EarthMoonSurrounding from './components/EarthMoonSurrounding.vue';
</script>
<style lang="less">
*{
margin: 0;
padding: 0;
}
</style>
初始化three.js基础代码
three.js开启必须用到的基础代码如下:
导入three库:
import * as THREE from 'three'
初始化场景:
const scene = new THREE.Scene()
初始化相机:
const camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,200)
camera.position.set(10,5,20)
初始化渲染器:
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth,window.innerHeight)
监听屏幕大小的改变,修改渲染器的宽高和相机的比例:
window.addEventListener("resize",()=>{
renderer.setSize(window.innerWidth,window.innerHeight)
camera.aspect = window.innerWidth/window.innerHeight
camera.updateProjectionMatrix()
})
设置渲染函数:
const render = () =>{
requestAnimationFrame(render);
renderer.render(scene, camera);
}
进行挂载:
onMounted(()=>{
render()
})
ok,写完基础代码之后,接下来开始具体的Demo实操。
创建月球模型
首先这里通过直接给画布的canvas标签添加css样式,给其一个背景图片作为背景:
canvas{
background-image: url('../../public/images/star.jpg');
background-size: cover;
}
然后使用TextureLoader用于加载图片纹理的工具,将图片文件加载为 Three.js 中的纹理对象,并且可以将纹理应用到场景中的任何物体上,从而实现更加生动的渲染效果。
// 创建月球
const moonGeometry = new THREE.SphereGeometry(MOON_RADIUS,16,16)
const moonMaterial = new THREE.MeshPhongMaterial({
map: textureLoader.load('/public/textures/planets/moon_1024.jpg')
})
const moon = new THREE.Mesh(moonGeometry, moonMaterial)
moon.receiveShadow = true
moon.castShadow = true;
scene.add(moon)
创建好模型之后给场景中添加光源:
// 创建添加聚光灯光源
const dirLight = new THREE.SpotLight(0xffffff)
dirLight.position.set(0,0,10)
dirLight.intensity = 1
dirLight.castShadow = true
scene.add(dirLight)
// 添加环境光
const aLight = new THREE.AmbientLight(0xffffff)
aLight.intensity = 0.03
scene.add(aLight)
在渲染函数中通过getElapsedTime方法计算动画经过的时间或者控制动画的播放速度,来动态改变月球位置,让其绕圆周开始运动。
let clock = new THREE.Clock();
// 设置渲染函数
const render = () =>{
const elapsed = clock.getElapsedTime()
// 月球旋转
moon.position.set(Math.sin(elapsed)*5,0,Math.cos(elapsed)*5)
renderer.render(scene,camera)
requestAnimationFrame(render)
}
添加地球模型
这里也采用TextureLoader用于加载图片纹理的工具,将图片文件加载为 Three.js 中的纹理对象,并且可以将纹理应用到场景中的任何物体上,从而实现更加生动的渲染效果。
// 创建地球
const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS,16,16)
const earthMaterial = new THREE.MeshPhongMaterial({
map: textureLoader.load('/public/textures/planets/earth_atmos_2048.jpg'),
specularMap: textureLoader.load('/public/textures/planets/earth_specular_2048.jpg'),
normalMap: textureLoader.load('/public/textures/planets/earth_normal_2048.jpg'),
})
const earth = new THREE.Mesh(earthGeometry,earthMaterial)
earth.receiveShadow = true
earth.castShadow = true
scene.add(earth)
这里依然在render渲染函数处通过getElapsedTime方法计算动画经过的时间或者控制动画的播放速度,来动态改变地球自转速度,让自转运动。
let clock = new THREE.Clock();
let oldtime = 0
// 设置渲染函数
const render = () =>{
const elapsed = clock.getElapsedTime()
// 月球旋转
moon.position.set(Math.sin(elapsed)*5,0,Math.cos(elapsed)*5)
// 地球自转
let axis = new THREE.Vector3(0,1,0)
earth.rotateOnAxis(axis, (elapsed - oldtime) * Math.PI / 10)
oldtime = elapsed
requestAnimationFrame(render)
}
添加模型标签
在 Three.js 中,CSS2DRenderer 是一个用于将 HTML 元素渲染成 2D 贴图,并将其添加到 Three.js 场景中的工具类。它可以帮助我们更方便地在 Three.js 场景中添加 HTML 元素,比如显示标签、文字等。CSS2DRenderer 类似于 Three.js 中的 WebGLRenderer,但是它渲染的不是 Three.js 的 3D 对象,而是 HTML 元素。
import { CSS2DRenderer} from 'three/examples/jsm/renderers/CSS2DRenderer'
// 创建标签选择器
const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight)
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
document.body.appendChild(labelRenderer.domElement)
// 给CSS2DRenderer添加控制器
const controls1 = new OrbitControls(camera,labelRenderer.domElement)
controls1.enableDamping = true
CSS2DObject 则是可以在 Three.js 场景中创建的一个 CSS2D 元素。它类似于 Three.js 中的 Mesh,但是它表示的不是三维物体,而是一个 CSS 元素。你可以通过创建 CSS2DObject 实例,将其添加到 Three.js 的场景中,并设置其内部的 HTML 元素,从而在 Three.js 场景中显示 HTML 内容。
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
// 创建月球标签
const moonDiv = document.createElement('div');
moonDiv.className = 'label';
moonDiv.textContent = 'Moon';
const moonLabel = new CSS2DObject(moonDiv)
moonLabel.position.set(0, MOON_RADIUS + 0.5, 0);
moon.add(moonLabel)
// 创建地球标签
const earthDiv = document.createElement('div');
earthDiv.className = 'label';
earthDiv.textContent = 'Earch';
const eartchLabel = new CSS2DObject(earthDiv)
eartchLabel.position.set(0, EARTH_RADIUS + 0.5, 0);
earth.add(eartchLabel)
然后给出直接设置类名的样式:
最后在渲染函数中加载渲染标签函数 :
demo做完,给出本案例的完整代码:(获取素材也可以私信博主)
<script setup>
import * as THREE from 'three'
import { onMounted } from 'vue';
// 控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// 文字2D渲染器
import { CSS2DRenderer,CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,200)
camera.position.set(10,5,20)
// 创建渲染器
const renderer = new THREE.WebGLRenderer({
alpha: true
})
renderer.setPixelRatio(window.devicePixelRatio) // 根据屏幕显示像素比
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled = true // 渲染阴影
document.body.appendChild(renderer.domElement)
window.addEventListener("resize",()=>{
renderer.setSize(window.innerWidth,window.innerHeight)
camera.aspect = window.innerWidth/window.innerHeight
camera.updateProjectionMatrix()
})
// 设置控制器
const controls = new OrbitControls(camera,renderer.domElement)
controls.enableDamping = true
let clock = new THREE.Clock();
let oldtime = 0
// 设置渲染函数
const render = () =>{
const elapsed = clock.getElapsedTime()
// 月球旋转
moon.position.set(Math.sin(elapsed)*5,0,Math.cos(elapsed)*5)
// 地球自转
let axis = new THREE.Vector3(0,1,0)
earth.rotateOnAxis(axis, (elapsed - oldtime) * Math.PI / 10)
oldtime = elapsed
renderer.render(scene,camera)
labelRenderer.render(scene, camera)
requestAnimationFrame(render)
}
// 设置地球和月球的半径大小
const EARTH_RADIUS = 2.5
const MOON_RADIUS = 0.27
// 实例化纹理加载器
const textureLoader = new THREE.TextureLoader()
// 创建标签选择器
const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight)
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
document.body.appendChild(labelRenderer.domElement)
// 给CSS2DRenderer添加控制器
const controls1 = new OrbitControls(camera,labelRenderer.domElement)
controls1.enableDamping = true
// 创建月球
const moonGeometry = new THREE.SphereGeometry(MOON_RADIUS,16,16)
const moonMaterial = new THREE.MeshPhongMaterial({
map: textureLoader.load('/public/textures/planets/moon_1024.jpg')
})
const moon = new THREE.Mesh(moonGeometry, moonMaterial)
moon.receiveShadow = true
moon.castShadow = true;
scene.add(moon)
// 创建月球标签
const moonDiv = document.createElement('div');
moonDiv.className = 'label';
moonDiv.textContent = 'Moon';
const moonLabel = new CSS2DObject(moonDiv)
moonLabel.position.set(0, MOON_RADIUS + 0.5, 0);
moon.add(moonLabel)
// 创建地球
const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS,16,16)
const earthMaterial = new THREE.MeshPhongMaterial({
map: textureLoader.load('/public/textures/planets/earth_atmos_2048.jpg'),
specularMap: textureLoader.load('/public/textures/planets/earth_specular_2048.jpg'),
normalMap: textureLoader.load('/public/textures/planets/earth_normal_2048.jpg'),
})
const earth = new THREE.Mesh(earthGeometry,earthMaterial)
earth.receiveShadow = true
earth.castShadow = true
scene.add(earth)
// 创建地球标签
const earthDiv = document.createElement('div');
earthDiv.className = 'label';
earthDiv.textContent = 'Earch';
const eartchLabel = new CSS2DObject(earthDiv)
eartchLabel.position.set(0, EARTH_RADIUS + 0.5, 0);
earth.add(eartchLabel)
// 创建添加聚光灯光源
const dirLight = new THREE.SpotLight(0xffffff)
dirLight.position.set(0,0,10)
dirLight.intensity = 1
dirLight.castShadow = true
scene.add(dirLight)
// 添加环境光
const aLight = new THREE.AmbientLight(0xffffff)
aLight.intensity = 0.03
scene.add(aLight)
onMounted(()=>{
render()
})
</script>
<style lang="less">
canvas{
background-image: url('../../public/images/star.jpg');
background-size: cover;
}
.label {
color: #fff;
font-size: 16px;
}
</style>