demos
This commit is contained in:
45
README.md
45
README.md
@@ -2,24 +2,51 @@
|
|||||||
|
|
||||||
## three.js 是什么
|
## three.js 是什么
|
||||||
|
|
||||||
|
https://threejs.org/manual/#zh/fundamentals
|
||||||
|
|
||||||
## 为什么选择 three.js
|
## 为什么选择 three.js
|
||||||
|
|
||||||
|
- WebGL
|
||||||
|
- https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API
|
||||||
|
- three.js
|
||||||
|
- https://threejs.org/
|
||||||
|
- http://www.webgl3d.cn/
|
||||||
|
- Babylon.js
|
||||||
|
- https://www.babylonjs.com/
|
||||||
|
- https://cnbabylon.com/
|
||||||
|
|
||||||
## three.js 核心概念
|
## three.js 核心概念
|
||||||
|
|
||||||
|
https://threejs.org/manual/#zh/fundamentals
|
||||||
|
|
||||||
### Scene 场景
|
### Scene 场景
|
||||||
### Camera 相机
|
### Camera 相机
|
||||||
### Renderer 渲染器
|
### Renderer 渲染器
|
||||||
|
|
||||||
|
## three.js 常用类/对象
|
||||||
|
|
||||||
|
### Object3D
|
||||||
|
### Material
|
||||||
|
https://threejs.org/manual/#zh/materials
|
||||||
|
|
||||||
|
### Texture
|
||||||
|
### OrbitControls
|
||||||
|
|
||||||
## Demos
|
## Demos
|
||||||
### 基础几何体
|
|
||||||
### 材质与纹理
|
### 加载GLB模型
|
||||||
### 光照与阴影
|
### 添加灯光
|
||||||
### 动画与交互
|
### 添加控制器、AxesHelper
|
||||||
### 粒子系统
|
### 设置地面
|
||||||
|
### 地面上设置光效
|
||||||
|
### 模型漫游(巡检业务)
|
||||||
|
### [x] 模型动画(模型本身存在动画)
|
||||||
|
### 修改材质(设备告警业务)
|
||||||
|
### 粒子效果(蒸汽)
|
||||||
|
### 模型与Web的交互
|
||||||
### 后期处理效果
|
### 后期处理效果
|
||||||
### 漫游 视角切换
|
|
||||||
### 标签
|
|
||||||
### 公共函数抽象
|
### 公共函数抽象
|
||||||
|
|
||||||
## 如何与建模沟通
|
|
||||||
|
|
||||||
## blender使用
|
## blender使用
|
||||||
|
|
||||||
|
## 如何与建模沟通
|
||||||
|
|||||||
0
src/composes/index.ts
Normal file
0
src/composes/index.ts
Normal file
0
src/composes/three/dom.ts
Normal file
0
src/composes/three/dom.ts
Normal file
2
src/composes/three/index.ts
Normal file
2
src/composes/three/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './loader'
|
||||||
|
export * from './texture'
|
||||||
5
src/composes/three/interface.ts
Normal file
5
src/composes/three/interface.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import type * as THREE from 'three'
|
||||||
|
|
||||||
|
export interface ScenceOptions {
|
||||||
|
background?: THREE.Texture<unknown> | THREE.CubeTexture | THREE.Color | null
|
||||||
|
}
|
||||||
13
src/composes/three/loader.ts
Normal file
13
src/composes/three/loader.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import * as THREE from 'three'
|
||||||
|
|
||||||
|
export function useThreeLoader() {
|
||||||
|
const textureLoader = new THREE.TextureLoader()
|
||||||
|
|
||||||
|
return {
|
||||||
|
textureLoader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const threeLoaders = useThreeLoader()
|
||||||
|
|
||||||
|
export { threeLoaders }
|
||||||
5
src/composes/three/mesh.ts
Normal file
5
src/composes/three/mesh.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import * as THREE from 'three'
|
||||||
|
|
||||||
|
export function useThreeMesh() {
|
||||||
|
|
||||||
|
}
|
||||||
9
src/composes/three/scene.ts
Normal file
9
src/composes/three/scene.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import * as THREE from 'three'
|
||||||
|
|
||||||
|
export function useThreeScene() {
|
||||||
|
function createScene(options) {
|
||||||
|
const scene = new THREE.Scene()
|
||||||
|
}
|
||||||
|
|
||||||
|
return { createScene }
|
||||||
|
}
|
||||||
22
src/composes/three/texture.ts
Normal file
22
src/composes/three/texture.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import type * as THREE from 'three'
|
||||||
|
import { threeLoaders } from './loader'
|
||||||
|
|
||||||
|
export function useThreeTexture() {
|
||||||
|
// 加载纹理贴图
|
||||||
|
function loadTexture(src: string) {
|
||||||
|
return new Promise<THREE.Texture>((resolve, reject) => {
|
||||||
|
threeLoaders.textureLoader.load(
|
||||||
|
src,
|
||||||
|
(texture) => {
|
||||||
|
resolve(texture)
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
(err) => {
|
||||||
|
reject(err)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { loadTexture }
|
||||||
|
}
|
||||||
@@ -9,6 +9,61 @@ const router = createRouter({
|
|||||||
name: 'home',
|
name: 'home',
|
||||||
component: HomeView,
|
component: HomeView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/three/1',
|
||||||
|
name: 'three-1',
|
||||||
|
component: () => import('../views/three/1/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/2',
|
||||||
|
name: 'three-2',
|
||||||
|
component: () => import('../views/three/2/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/3',
|
||||||
|
name: 'three-3',
|
||||||
|
component: () => import('../views/three/3/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/4',
|
||||||
|
name: 'three-4',
|
||||||
|
component: () => import('../views/three/4/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/5',
|
||||||
|
name: 'three-5',
|
||||||
|
component: () => import('../views/three/5/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/6',
|
||||||
|
name: 'three-6',
|
||||||
|
component: () => import('../views/three/6/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/7',
|
||||||
|
name: 'three-7',
|
||||||
|
component: () => import('../views/three/7/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/8',
|
||||||
|
name: 'three-8',
|
||||||
|
component: () => import('../views/three/8/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/9',
|
||||||
|
name: 'three-9',
|
||||||
|
component: () => import('../views/three/9/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/10',
|
||||||
|
name: 'three-10',
|
||||||
|
component: () => import('../views/three/10/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/three/11',
|
||||||
|
name: 'three-11',
|
||||||
|
component: () => import('../views/three/11/index.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/3d-point',
|
path: '/3d-point',
|
||||||
name: '3d-point',
|
name: '3d-point',
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const demos = [{ name: '3D Point', path: '/3d-point' }]
|
const demos = [
|
||||||
|
{ name: '加载GLB模型', path: '/three/1' },
|
||||||
|
{ name: '添加灯光', path: '/three/2' },
|
||||||
|
{ name: '添加控制器、AxesHelper', path: '/three/3' },
|
||||||
|
{ name: '设置地面', path: '/three/3' },
|
||||||
|
{ name: '地面上设置光效', path: '/three/4' },
|
||||||
|
{ name: '模型漫游(巡检业务)', path: '/three/5' },
|
||||||
|
{ name: '模型动画(模型本身存在动画)', path: '/three/6' },
|
||||||
|
{ name: '漫游 修改材质(设备告警业务)', path: '/three/7' },
|
||||||
|
{ name: '粒子效果(蒸汽)', path: '/three/8' },
|
||||||
|
{ name: '模型与Web的交互', path: '/three/9' },
|
||||||
|
{ name: '后期处理效果', path: '/three/10' },
|
||||||
|
{ name: '公共函数抽象', path: '/three/11' },
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
75
src/views/three/1/index.vue
Normal file
75
src/views/three/1/index.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import * as THREE from 'three'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||||
|
import { onMounted, shallowRef } from 'vue'
|
||||||
|
|
||||||
|
const threeRef = shallowRef<HTMLCanvasElement>()
|
||||||
|
|
||||||
|
function initModel() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
|
||||||
|
const scene = new THREE.Scene()
|
||||||
|
scene.background = new THREE.Color(0xFFFFFF)
|
||||||
|
|
||||||
|
// 创建透视相机 PerspectiveCamera(视野角度, 宽高比, 近截面, 远截面)
|
||||||
|
// 透视相机更符合人眼的视觉效果 近大远小
|
||||||
|
const camera = new THREE.PerspectiveCamera(
|
||||||
|
75,
|
||||||
|
width / height,
|
||||||
|
0.1,
|
||||||
|
1000,
|
||||||
|
)
|
||||||
|
camera.position.set(0, 2, 12)
|
||||||
|
|
||||||
|
const renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
// 开启阴影贴图
|
||||||
|
renderer.shadowMap.enabled = true
|
||||||
|
// 设置阴影贴图类型 THREE.PCFSoftShadowMap=柔和阴影 THREE.PCFShadowMap=硬阴影
|
||||||
|
// 柔和阴影计算量更大,性能更低
|
||||||
|
// 硬阴影计算量更小,性能更高
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
|
threeRef.value!.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
const loader = new GLTFLoader()
|
||||||
|
loader.load('/mzjc_bansw.glb', (gltf) => {
|
||||||
|
scene.add(gltf.scene)
|
||||||
|
})
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
// 60Hz 1000/60=16.6ms
|
||||||
|
requestAnimationFrame(animate)
|
||||||
|
// 执行渲染
|
||||||
|
renderer.render(scene, camera)
|
||||||
|
// 适配高分辨率屏幕
|
||||||
|
renderer.setPixelRatio(width / height)
|
||||||
|
// 设置渲染器尺寸
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
// 更新相机宽高比
|
||||||
|
camera.aspect = width / height
|
||||||
|
// 更新相机投影矩阵
|
||||||
|
camera.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(initModel)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="threeRef" class="model" />
|
||||||
|
<router-link
|
||||||
|
to="/three/2"
|
||||||
|
class="position-fixed left-20px top-20px"
|
||||||
|
>
|
||||||
|
添加灯光
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.model {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
6
src/views/three/10/index.vue
Normal file
6
src/views/three/10/index.vue
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>1</div>
|
||||||
|
</template>
|
||||||
6
src/views/three/11/index.vue
Normal file
6
src/views/three/11/index.vue
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>1</div>
|
||||||
|
</template>
|
||||||
183
src/views/three/2/index.vue
Normal file
183
src/views/three/2/index.vue
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import * as THREE from 'three'
|
||||||
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||||
|
import { onMounted, shallowRef } from 'vue'
|
||||||
|
|
||||||
|
const threeRef = shallowRef<HTMLCanvasElement>()
|
||||||
|
|
||||||
|
let scene: THREE.Scene | undefined
|
||||||
|
let camera: THREE.PerspectiveCamera | undefined
|
||||||
|
let renderer: THREE.WebGLRenderer | undefined
|
||||||
|
|
||||||
|
function getElementSize() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
|
||||||
|
return { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
function initModel() {
|
||||||
|
const { width, height } = getElementSize()
|
||||||
|
|
||||||
|
scene = new THREE.Scene()
|
||||||
|
scene.background = new THREE.Color(0x000000)
|
||||||
|
|
||||||
|
camera = new THREE.PerspectiveCamera(
|
||||||
|
75,
|
||||||
|
width / height,
|
||||||
|
0.1,
|
||||||
|
1000,
|
||||||
|
)
|
||||||
|
camera.position.set(0, 2, 12)
|
||||||
|
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
renderer.shadowMap.enabled = true
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
|
threeRef.value!.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
const loader = new GLTFLoader()
|
||||||
|
loader.load('/mzjc_bansw.glb', (gltf) => {
|
||||||
|
scene!.add(gltf.scene)
|
||||||
|
})
|
||||||
|
|
||||||
|
const controls = new OrbitControls(camera, renderer.domElement)
|
||||||
|
controls.enableDamping = true
|
||||||
|
controls.dampingFactor = 0.05
|
||||||
|
|
||||||
|
const axesHelper = new THREE.AxesHelper(5)
|
||||||
|
scene!.add(axesHelper)
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
requestAnimationFrame(animate)
|
||||||
|
renderer!.render(scene!, camera!)
|
||||||
|
renderer!.setPixelRatio(width / height)
|
||||||
|
renderer!.setSize(width, height)
|
||||||
|
camera!.aspect = width / height
|
||||||
|
camera!.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSphereGeometry(x: number, y: number, z: number) {
|
||||||
|
const geometry = new THREE.SphereGeometry(15, 32, 16)
|
||||||
|
const material = new THREE.MeshBasicMaterial({ color: 0xFFFF00 })
|
||||||
|
const sphere = new THREE.Mesh(geometry, material)
|
||||||
|
sphere.scale.set(0.005, 0.005, 0.005)
|
||||||
|
sphere.position.set(x, y, z)
|
||||||
|
scene!.add(sphere)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加环境光
|
||||||
|
// 均匀照亮整个场景,无方向、无阴影
|
||||||
|
// 提供基础亮度,防止未被其他光照到的区域全黑
|
||||||
|
function addAmbientLight() {
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.5) // 颜色, 强度
|
||||||
|
scene!.add(ambientLight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平行光
|
||||||
|
// 模拟太阳光,所有光线平行照射,可产生阴影
|
||||||
|
// 主光源,适合户外或强定向照明
|
||||||
|
// 光源位置决定阴影位置
|
||||||
|
function addDirectionalLight() {
|
||||||
|
const directionalLight = new THREE.DirectionalLight(0x00FFFF, 0.5) // 颜色, 强度
|
||||||
|
directionalLight.position.set(1, 2, 8) // 光源位置(方向由位置决定)
|
||||||
|
// 启用阴影(需开启渲染器和物体的阴影支持)
|
||||||
|
directionalLight.castShadow = true
|
||||||
|
scene!.add(directionalLight)
|
||||||
|
|
||||||
|
const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight)
|
||||||
|
scene!.add(directionalLightHelper)
|
||||||
|
|
||||||
|
addSphereGeometry(1, 2, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点光源
|
||||||
|
// 向各个方向均匀发射光线,可产生阴影
|
||||||
|
// 模拟灯泡等局部光源效果
|
||||||
|
// 光源位置决定阴影位置
|
||||||
|
// 光照强度随距离衰减 模拟现实世界中光强随传播距离自然衰减的物理行为
|
||||||
|
function addPointLight() {
|
||||||
|
const pointLight = new THREE.PointLight(0x00FFFF, 10, 1000) // 颜色, 强度, 距离衰减
|
||||||
|
pointLight.position.set(1, 2, 8) // 光源位置
|
||||||
|
// 启用阴影(需开启渲染器和物体的阴影支持)
|
||||||
|
pointLight.castShadow = true
|
||||||
|
scene!.add(pointLight)
|
||||||
|
|
||||||
|
const pointLightHelper = new THREE.PointLightHelper(pointLight)
|
||||||
|
scene!.add(pointLightHelper)
|
||||||
|
|
||||||
|
addSphereGeometry(1, 2, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 聚光灯
|
||||||
|
// 定向锥形光束,可产生阴影
|
||||||
|
// 模拟手电筒或舞台聚光灯效果
|
||||||
|
function addSpotLight() {
|
||||||
|
const spotLight = new THREE.SpotLight(0x00FFFF, 100)// 颜色, 强度
|
||||||
|
spotLight.position.set(1, 2, 8)
|
||||||
|
spotLight.angle = Math.PI / 6 // 光锥角度(弧度)
|
||||||
|
spotLight.penumbra = 0.2 // 半影模糊程度 (0~1)
|
||||||
|
spotLight.decay = 2 // 衰减指数
|
||||||
|
spotLight.distance = 100 // 最大距离
|
||||||
|
scene!.add(spotLight)
|
||||||
|
|
||||||
|
// 可选:显示光锥辅助线
|
||||||
|
const spotLightHelper = new THREE.SpotLightHelper(spotLight)
|
||||||
|
scene!.add(spotLightHelper)
|
||||||
|
|
||||||
|
addSphereGeometry(1, 2, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 半球光
|
||||||
|
// 模拟天空和地面的漫反射光照效果
|
||||||
|
// 天空颜色从上方照射,地面颜色从下方反射
|
||||||
|
// 柔和的户外环境光,比 AmbientLight 更真实
|
||||||
|
function addHemisphereLight() {
|
||||||
|
const hemisphereLight = new THREE.HemisphereLight(0x00FFFF, 0xFFFFBB, 1) // 天空颜色, 地面颜色, 强度
|
||||||
|
scene!.add(hemisphereLight)
|
||||||
|
|
||||||
|
const hemisphereLightHelper = new THREE.HemisphereLightHelper(hemisphereLight, 5)
|
||||||
|
scene!.add(hemisphereLightHelper)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(initModel)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="threeRef" class="model" />
|
||||||
|
<router-link
|
||||||
|
to="/three/3"
|
||||||
|
class="position-fixed left-20px top-20px"
|
||||||
|
>
|
||||||
|
添加控制器、AxesHelper
|
||||||
|
</router-link>
|
||||||
|
<div class="position-fixed right-20px top-20px flex gap-12px">
|
||||||
|
<button @click="addAmbientLight">
|
||||||
|
添加环境光
|
||||||
|
</button>
|
||||||
|
<button @click="addDirectionalLight">
|
||||||
|
添加平行光
|
||||||
|
</button>
|
||||||
|
<button @click="addPointLight">
|
||||||
|
添加点光源
|
||||||
|
</button>
|
||||||
|
<button @click="addSpotLight">
|
||||||
|
添加聚光灯
|
||||||
|
</button>
|
||||||
|
<button @click="addHemisphereLight">
|
||||||
|
添加半球光
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.model {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
86
src/views/three/3/index.vue
Normal file
86
src/views/three/3/index.vue
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import * as THREE from 'three'
|
||||||
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||||
|
import { onMounted, shallowRef } from 'vue'
|
||||||
|
|
||||||
|
const threeRef = shallowRef<HTMLCanvasElement>()
|
||||||
|
|
||||||
|
let scene: THREE.Scene | undefined
|
||||||
|
let camera: THREE.PerspectiveCamera | undefined
|
||||||
|
let renderer: THREE.WebGLRenderer | undefined
|
||||||
|
|
||||||
|
function getElementSize() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
|
||||||
|
return { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
function initModel() {
|
||||||
|
const { width, height } = getElementSize()
|
||||||
|
|
||||||
|
scene = new THREE.Scene()
|
||||||
|
scene.background = new THREE.Color(0x000000)
|
||||||
|
|
||||||
|
camera = new THREE.PerspectiveCamera(
|
||||||
|
75,
|
||||||
|
width / height,
|
||||||
|
0.1,
|
||||||
|
1000,
|
||||||
|
)
|
||||||
|
camera.position.set(0, 2, 12)
|
||||||
|
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
renderer.shadowMap.enabled = true
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
|
threeRef.value!.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
const loader = new GLTFLoader()
|
||||||
|
loader.load('/mzjc_bansw.glb', (gltf) => {
|
||||||
|
scene!.add(gltf.scene)
|
||||||
|
})
|
||||||
|
|
||||||
|
const controls = new OrbitControls(camera, renderer.domElement)
|
||||||
|
controls.enableDamping = true
|
||||||
|
controls.dampingFactor = 0.05
|
||||||
|
|
||||||
|
const axesHelper = new THREE.AxesHelper(5)
|
||||||
|
scene!.add(axesHelper)
|
||||||
|
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xFFFFFF, 1.2) // 颜色, 强度
|
||||||
|
scene!.add(ambientLight)
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
requestAnimationFrame(animate)
|
||||||
|
renderer!.render(scene!, camera!)
|
||||||
|
renderer!.setPixelRatio(width / height)
|
||||||
|
renderer!.setSize(width, height)
|
||||||
|
camera!.aspect = width / height
|
||||||
|
camera!.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(initModel)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="threeRef" class="model" />
|
||||||
|
<router-link
|
||||||
|
to="/three/4"
|
||||||
|
class="position-fixed left-20px top-20px"
|
||||||
|
>
|
||||||
|
设置地面
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.model {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
135
src/views/three/4/index.vue
Normal file
135
src/views/three/4/index.vue
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import * as THREE from 'three'
|
||||||
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||||
|
import { onMounted, shallowRef } from 'vue'
|
||||||
|
|
||||||
|
const threeRef = shallowRef<HTMLCanvasElement>()
|
||||||
|
|
||||||
|
let scene: THREE.Scene | undefined
|
||||||
|
let camera: THREE.PerspectiveCamera | undefined
|
||||||
|
let renderer: THREE.WebGLRenderer | undefined
|
||||||
|
|
||||||
|
function getElementSize() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
|
||||||
|
return { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
function initModel() {
|
||||||
|
const { width, height } = getElementSize()
|
||||||
|
|
||||||
|
scene = new THREE.Scene()
|
||||||
|
scene.background = new THREE.Color(0x000000)
|
||||||
|
|
||||||
|
camera = new THREE.PerspectiveCamera(
|
||||||
|
75,
|
||||||
|
width / height,
|
||||||
|
0.1,
|
||||||
|
1000,
|
||||||
|
)
|
||||||
|
camera.position.set(0, 2, 12)
|
||||||
|
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
renderer.shadowMap.enabled = true
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
|
threeRef.value!.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
const loader = new GLTFLoader()
|
||||||
|
loader.load('/mzjc_bansw.glb', (gltf) => {
|
||||||
|
scene!.add(gltf.scene)
|
||||||
|
})
|
||||||
|
|
||||||
|
const controls = new OrbitControls(camera, renderer.domElement)
|
||||||
|
controls.enableDamping = true
|
||||||
|
controls.dampingFactor = 0.05
|
||||||
|
|
||||||
|
const axesHelper = new THREE.AxesHelper(5)
|
||||||
|
scene!.add(axesHelper)
|
||||||
|
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xFFFFFF, 1.2) // 颜色, 强度
|
||||||
|
scene!.add(ambientLight)
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
requestAnimationFrame(animate)
|
||||||
|
renderer!.render(scene!, camera!)
|
||||||
|
renderer!.setPixelRatio(width / height)
|
||||||
|
renderer!.setSize(width, height)
|
||||||
|
camera!.aspect = width / height
|
||||||
|
camera!.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPlaneGeometry() {
|
||||||
|
// 创建地面几何体:宽度 20,高度 20,细分 1x1(足够平坦)
|
||||||
|
const groundGeometry = new THREE.PlaneGeometry(20, 20, 1, 1)
|
||||||
|
// 使用标准材质(支持光照和阴影)
|
||||||
|
const groundMaterial = new THREE.MeshStandardMaterial({
|
||||||
|
color: 0xAAAAAA, // 灰色
|
||||||
|
roughness: 0.8,
|
||||||
|
metalness: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建网格
|
||||||
|
const ground = new THREE.Mesh(groundGeometry, groundMaterial)
|
||||||
|
|
||||||
|
// 旋转 90 度使其平放在 XZ 平面上(默认 PlaneGeometry 在 XY 平面)
|
||||||
|
ground.rotation.x = -Math.PI / 2
|
||||||
|
|
||||||
|
// 可选:启用接收阴影(如果场景中有灯光和阴影)
|
||||||
|
ground.receiveShadow = true
|
||||||
|
|
||||||
|
// 添加到场景
|
||||||
|
scene!.add(ground)
|
||||||
|
|
||||||
|
const gridHelper = new THREE.GridHelper(100, 20, 0x444444, 0x222222)
|
||||||
|
scene!.add(gridHelper)
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBoxGeometry() {
|
||||||
|
// 创建一个 20×0.2×20 的长方体(宽×高×深)
|
||||||
|
const groundGeometry = new THREE.BoxGeometry(20, 0.2, 20)
|
||||||
|
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x556B2F })
|
||||||
|
|
||||||
|
const ground = new THREE.Mesh(groundGeometry, groundMaterial)
|
||||||
|
ground.position.y = -0.1 // 让顶部对齐 Y=0 平面 几何体的原点(pivot point)默认在它的中心
|
||||||
|
ground.receiveShadow = true
|
||||||
|
|
||||||
|
scene!.add(ground)
|
||||||
|
|
||||||
|
const gridHelper = new THREE.GridHelper(100, 20, 0x444444, 0x222222)
|
||||||
|
scene!.add(gridHelper)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(initModel)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="threeRef" class="model" />
|
||||||
|
<router-link
|
||||||
|
to="/three/5"
|
||||||
|
class="position-fixed left-20px top-20px"
|
||||||
|
>
|
||||||
|
地面上设置光效
|
||||||
|
</router-link>
|
||||||
|
<div class="position-fixed right-20px top-20px flex gap-12px">
|
||||||
|
<button @click="addPlaneGeometry">
|
||||||
|
添加2D地面
|
||||||
|
</button>
|
||||||
|
<button @click="addBoxGeometry">
|
||||||
|
添加有厚度的地面
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.model {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
273
src/views/three/5/index.vue
Normal file
273
src/views/three/5/index.vue
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import * as THREE from 'three'
|
||||||
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||||
|
import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
|
||||||
|
|
||||||
|
// ========== 常量定义 ==========
|
||||||
|
const GROUND_SIZE = 30
|
||||||
|
const GROUND_THICKNESS = 0.2
|
||||||
|
const LINE_COLORS = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF]
|
||||||
|
const LINE_SPEED = 0.1
|
||||||
|
const LINE_DELAY = 300
|
||||||
|
|
||||||
|
// ========== 类型定义 ==========
|
||||||
|
interface LineState {
|
||||||
|
mesh: THREE.Mesh
|
||||||
|
direction: number
|
||||||
|
startDelay: number
|
||||||
|
elapsed: number
|
||||||
|
axis: 'x' | 'z'
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 状态变量 ==========
|
||||||
|
const threeRef = shallowRef<HTMLCanvasElement>()
|
||||||
|
|
||||||
|
let scene: THREE.Scene
|
||||||
|
let camera: THREE.PerspectiveCamera
|
||||||
|
let renderer: THREE.WebGLRenderer
|
||||||
|
let controls: OrbitControls
|
||||||
|
let animationId: number
|
||||||
|
|
||||||
|
const lineStates: LineState[] = []
|
||||||
|
|
||||||
|
function getElementSize() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
|
||||||
|
return { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 初始化场景 ==========
|
||||||
|
function initModel() {
|
||||||
|
const { width, height } = getElementSize()
|
||||||
|
|
||||||
|
// 创建场景
|
||||||
|
scene = new THREE.Scene()
|
||||||
|
scene.background = new THREE.Color(0x000000)
|
||||||
|
|
||||||
|
// 创建相机
|
||||||
|
camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
|
||||||
|
camera.position.set(3, 8, 12)
|
||||||
|
|
||||||
|
// 创建渲染器
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio)
|
||||||
|
renderer.shadowMap.enabled = true
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
|
renderer.localClippingEnabled = true
|
||||||
|
threeRef.value!.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
// 加载模型
|
||||||
|
const loader = new GLTFLoader()
|
||||||
|
loader.load('/mzjc_bansw.glb', (gltf) => {
|
||||||
|
scene.add(gltf.scene)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建控制器
|
||||||
|
controls = new OrbitControls(camera, renderer.domElement)
|
||||||
|
controls.enableDamping = true
|
||||||
|
controls.dampingFactor = 0.05
|
||||||
|
|
||||||
|
// 添加辅助工具
|
||||||
|
const axesHelper = new THREE.AxesHelper(5)
|
||||||
|
scene.add(axesHelper)
|
||||||
|
|
||||||
|
// 添加环境光
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xFFFFFF, 1.2)
|
||||||
|
scene.add(ambientLight)
|
||||||
|
|
||||||
|
// 添加地面
|
||||||
|
addGround()
|
||||||
|
|
||||||
|
// 启动动画循环
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 添加地面 ==========
|
||||||
|
function addGround() {
|
||||||
|
const geometry = new THREE.BoxGeometry(GROUND_SIZE, GROUND_THICKNESS, GROUND_SIZE)
|
||||||
|
const material = new THREE.MeshStandardMaterial({ color: 0x222222 })
|
||||||
|
const ground = new THREE.Mesh(geometry, material)
|
||||||
|
ground.position.y = -GROUND_THICKNESS / 2
|
||||||
|
ground.receiveShadow = true
|
||||||
|
scene.add(ground)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 添加地面光效 ==========
|
||||||
|
function addGroundLightEffect() {
|
||||||
|
// 创建裁剪平面
|
||||||
|
const clipPlanes = [
|
||||||
|
new THREE.Plane(new THREE.Vector3(1, 0, 0), GROUND_SIZE / 2),
|
||||||
|
new THREE.Plane(new THREE.Vector3(-1, 0, 0), GROUND_SIZE / 2),
|
||||||
|
new THREE.Plane(new THREE.Vector3(0, 0, 1), GROUND_SIZE / 2),
|
||||||
|
new THREE.Plane(new THREE.Vector3(0, 0, -1), GROUND_SIZE / 2),
|
||||||
|
]
|
||||||
|
|
||||||
|
const geometry = new THREE.BoxGeometry(GROUND_SIZE, 0.1, 0.5)
|
||||||
|
|
||||||
|
LINE_COLORS.forEach((color, index) => {
|
||||||
|
const material = new THREE.MeshBasicMaterial({
|
||||||
|
color: new THREE.Color(color),
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.15 * (index + 1) * Math.random(),
|
||||||
|
clippingPlanes: clipPlanes,
|
||||||
|
clipShadows: true,
|
||||||
|
depthWrite: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const offset = -GROUND_SIZE / 2 + (GROUND_SIZE / LINE_COLORS.length) * (index + 0.5)
|
||||||
|
const isEven = index % 2 === 0
|
||||||
|
|
||||||
|
// 创建X轴线条
|
||||||
|
const xMesh = new THREE.Mesh(geometry, material.clone())
|
||||||
|
xMesh.position.set(isEven ? GROUND_SIZE : -GROUND_SIZE, 0, offset)
|
||||||
|
xMesh.renderOrder = 1
|
||||||
|
scene.add(xMesh)
|
||||||
|
|
||||||
|
lineStates.push({
|
||||||
|
mesh: xMesh,
|
||||||
|
direction: isEven ? -1 : 1,
|
||||||
|
startDelay: index * LINE_DELAY,
|
||||||
|
elapsed: 0,
|
||||||
|
axis: 'x',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建Z轴线条
|
||||||
|
const zMesh = new THREE.Mesh(geometry, material.clone())
|
||||||
|
zMesh.position.set(offset, 0.01, isEven ? GROUND_SIZE : -GROUND_SIZE)
|
||||||
|
zMesh.rotation.y = Math.PI / 2
|
||||||
|
zMesh.renderOrder = 2
|
||||||
|
scene.add(zMesh)
|
||||||
|
|
||||||
|
lineStates.push({
|
||||||
|
mesh: zMesh,
|
||||||
|
direction: isEven ? -1 : 1,
|
||||||
|
startDelay: index * LINE_DELAY,
|
||||||
|
elapsed: 0,
|
||||||
|
axis: 'z',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 更新线条动画 ==========
|
||||||
|
function updateLines(deltaTime: number) {
|
||||||
|
lineStates.forEach((state) => {
|
||||||
|
state.elapsed += deltaTime
|
||||||
|
if (state.elapsed < state.startDelay)
|
||||||
|
return
|
||||||
|
|
||||||
|
const pos = state.axis === 'x' ? state.mesh.position.x : state.mesh.position.z
|
||||||
|
|
||||||
|
// 边界检测并反转方向
|
||||||
|
if (pos >= GROUND_SIZE)
|
||||||
|
state.direction = -1
|
||||||
|
else if (pos <= -GROUND_SIZE)
|
||||||
|
state.direction = 1
|
||||||
|
|
||||||
|
// 更新位置
|
||||||
|
const delta = LINE_SPEED * state.direction
|
||||||
|
if (state.axis === 'x') {
|
||||||
|
state.mesh.position.x += delta
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state.mesh.position.z += delta
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 主动画循环 ==========
|
||||||
|
let lastTime = 0
|
||||||
|
function animate(time = 0) {
|
||||||
|
animationId = requestAnimationFrame(animate)
|
||||||
|
|
||||||
|
const deltaTime = time - lastTime
|
||||||
|
lastTime = time
|
||||||
|
|
||||||
|
// 更新控制器
|
||||||
|
controls.update()
|
||||||
|
|
||||||
|
// 更新线条动画
|
||||||
|
updateLines(deltaTime)
|
||||||
|
|
||||||
|
// 响应式调整渲染器大小
|
||||||
|
const { width, height } = getElementSize()
|
||||||
|
if (camera.aspect !== width / height) {
|
||||||
|
camera.aspect = width / height
|
||||||
|
camera.updateProjectionMatrix()
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.render(scene, camera)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 清理资源 ==========
|
||||||
|
function cleanup() {
|
||||||
|
if (animationId)
|
||||||
|
cancelAnimationFrame(animationId)
|
||||||
|
if (controls)
|
||||||
|
controls.dispose()
|
||||||
|
if (renderer) {
|
||||||
|
renderer.dispose()
|
||||||
|
threeRef.value?.removeChild(renderer.domElement)
|
||||||
|
}
|
||||||
|
scene?.traverse((object) => {
|
||||||
|
if (object instanceof THREE.Mesh) {
|
||||||
|
object.geometry?.dispose()
|
||||||
|
if (Array.isArray(object.material)) {
|
||||||
|
object.material.forEach(m => m.dispose())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
object.material?.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
lineStates.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function pauseGroundLightEffect() {
|
||||||
|
cancelAnimationFrame(animationId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeGroundLightEffect() {
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function destoryGroundLightEffect() {
|
||||||
|
lineStates.forEach((state) => {
|
||||||
|
state.mesh.parent?.remove(state.mesh)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(initModel)
|
||||||
|
onBeforeUnmount(cleanup)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="threeRef" class="model" />
|
||||||
|
<router-link to="/three/5" class="position-fixed left-20px top-20px">
|
||||||
|
地面上设置光效
|
||||||
|
</router-link>
|
||||||
|
<div class="position-fixed right-20px top-20px flex gap-12px">
|
||||||
|
<button @click="addGroundLightEffect">
|
||||||
|
添加地面光效
|
||||||
|
</button>
|
||||||
|
<button @click="pauseGroundLightEffect">
|
||||||
|
暂停地面光效
|
||||||
|
</button>
|
||||||
|
<button @click="resumeGroundLightEffect">
|
||||||
|
继续地面光效
|
||||||
|
</button>
|
||||||
|
<button @click="destoryGroundLightEffect">
|
||||||
|
销毁地面光效
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.model {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
103
src/views/three/6/index.vue
Normal file
103
src/views/three/6/index.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import * as THREE from 'three'
|
||||||
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||||
|
import { onMounted, shallowRef } from 'vue'
|
||||||
|
|
||||||
|
const threeRef = shallowRef<HTMLCanvasElement>()
|
||||||
|
|
||||||
|
function initModel() {
|
||||||
|
const width = threeRef.value!.clientWidth
|
||||||
|
const height = threeRef.value!.clientHeight
|
||||||
|
|
||||||
|
const scene = new THREE.Scene()
|
||||||
|
scene.background = new THREE.Color(0x000000)
|
||||||
|
|
||||||
|
const camera = new THREE.PerspectiveCamera(
|
||||||
|
75,
|
||||||
|
width / height,
|
||||||
|
0.1,
|
||||||
|
1000,
|
||||||
|
)
|
||||||
|
camera.position.set(0.1, 0.1, 1)
|
||||||
|
|
||||||
|
const renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
renderer.shadowMap.enabled = true
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
|
threeRef.value!.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
const controls = new OrbitControls(camera, renderer.domElement)
|
||||||
|
controls.enableDamping = true
|
||||||
|
controls.dampingFactor = 0.05
|
||||||
|
|
||||||
|
const axesHelper = new THREE.AxesHelper(5)
|
||||||
|
scene.add(axesHelper)
|
||||||
|
|
||||||
|
// 环境光
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.5)
|
||||||
|
scene.add(ambientLight)
|
||||||
|
|
||||||
|
// 方向光(主光)
|
||||||
|
const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 0.8)
|
||||||
|
directionalLight.position.set(20, 30, 20)
|
||||||
|
directionalLight.castShadow = true
|
||||||
|
directionalLight.shadow.mapSize.set(2048, 2048)
|
||||||
|
directionalLight.shadow.camera.near = 0.5
|
||||||
|
directionalLight.shadow.camera.far = 50
|
||||||
|
directionalLight.shadow.camera.left = -20
|
||||||
|
directionalLight.shadow.camera.right = 20
|
||||||
|
directionalLight.shadow.camera.top = 20
|
||||||
|
directionalLight.shadow.camera.bottom = -20
|
||||||
|
scene.add(directionalLight)
|
||||||
|
|
||||||
|
// 点光源
|
||||||
|
const pointLight = new THREE.PointLight(0x409EFF, 0.5, 50)
|
||||||
|
pointLight.position.set(0, 10, 0)
|
||||||
|
scene.add(pointLight)
|
||||||
|
|
||||||
|
const loader = new GLTFLoader()
|
||||||
|
loader.load('/lxb_grp.glb', (gltf) => {
|
||||||
|
scene.add(gltf.scene)
|
||||||
|
|
||||||
|
const { animations = [] } = gltf
|
||||||
|
animations.forEach((clip) => {
|
||||||
|
const mixer = new THREE.AnimationMixer(gltf.scene)
|
||||||
|
const action = mixer.clipAction(clip)
|
||||||
|
action.play()
|
||||||
|
|
||||||
|
const clock = new THREE.Clock()
|
||||||
|
// 在动画循环中更新混合器
|
||||||
|
function updateMixer() {
|
||||||
|
requestAnimationFrame(updateMixer)
|
||||||
|
const delta = clock.getDelta()
|
||||||
|
mixer.update(delta)
|
||||||
|
}
|
||||||
|
updateMixer()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
requestAnimationFrame(animate)
|
||||||
|
renderer.render(scene, camera)
|
||||||
|
renderer.setSize(width, height)
|
||||||
|
camera.aspect = width / height
|
||||||
|
camera.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(initModel)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="threeRef" class="model" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.model {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
6
src/views/three/7/index.vue
Normal file
6
src/views/three/7/index.vue
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>1</div>
|
||||||
|
</template>
|
||||||
6
src/views/three/8/index.vue
Normal file
6
src/views/three/8/index.vue
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>1</div>
|
||||||
|
</template>
|
||||||
6
src/views/three/9/index.vue
Normal file
6
src/views/three/9/index.vue
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>1</div>
|
||||||
|
</template>
|
||||||
@@ -3,13 +3,11 @@ import { fileURLToPath, URL } from 'node:url'
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import Unocss from 'unocss/vite'
|
import Unocss from 'unocss/vite'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
vueDevTools(),
|
|
||||||
Unocss(),
|
Unocss(),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
Reference in New Issue
Block a user