import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js'
import gsap from 'gsap' // 导入动画库
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.set(0, 0, 40)
scene.add(camera)
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
scene.add(ambientLight)
// -----------------------------------------------------------------------
const gltfLoader = new GLTFLoader()
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('../public/draco/')
`
解码器的配置,指定解码器的类型为'js',即:JavaScript解码器
Draco也支持WebAssembly(Wasm)解码器,
这里,我们选择JavaScript版本。`
dracoLoader.setDecoderConfig({ type: 'js' })
`
预加载解码器,
将Draco加载器,与,GLTF加载器,结合使用之前,
需要预加载解码器,
因为,解码器可能需要一些时间从网络加载,并且我们希望,它在模型加载之前就已经准备好了。`
dracoLoader.preload()
gltfLoader.setDRACOLoader(dracoLoader)
let stem = null // 茎
let stem_1 = null // 茎
let stem_2 = null // 茎
let petal = null // 花瓣
let petal_1 = null // 花瓣
let petal_2 = null // 花瓣
let params = { value_0: 0,value_1: 0 }
gltfLoader.load('../public/assets/model-19/f4.glb', gltf_1 => {
// console.log('gltf_1=', gltf_1)
stem = gltf_1.scene.children[0]
petal = gltf_1.scene.children[1]
gltf_1.scene.rotation.x = Math.PI
// 遍历,gltf.scene中的所有对象
gltf_1.scene.traverse(item => {
// 水
if (item.material && item.material.name == 'Water') {
// item.scale.set(0.5, 0.5, 0.5) // 缩放比例,设置为0.8(在三个维度上)
item.material = new THREE.MeshStandardMaterial({
color: 'red',
// side: THREE.DoubleSide,
transparent: true,
opacity: 0.7,
depthWrite: false,
depthTest: false
})
}
// 茎
if (item.material && item.material.name == 'Stem') {
stem = item
}
// 花瓣
if (item.material && item.material.name == 'Petal') {
petal = item
}
})
gltfLoader.load('../public/assets/model-19/f2.glb', gltf_2 => {
gltf_2.scene.traverse(item => {
// 茎
if (item.material && item.material.name == 'Stem') {
stem_1 = item
stem.geometry.morphAttributes.position = [stem_1.geometry.attributes.position]
stem.updateMorphTargets()
}
// 花苞
if (item.material && item.material.name == 'Petal') {
petal_1 = item
petal.geometry.morphAttributes.position = [petal_1.geometry.attributes.position]
petal.updateMorphTargets()
}
// 开花
gltfLoader.load('../public/assets/model-19/f1.glb', gltf => {
gltf.scene.traverse(item => {
if (item.material && item.material.name == 'Stem') {
stem_2 = item
stem.geometry.morphAttributes.position.push(stem_2.geometry.attributes.position)
stem.updateMorphTargets()
}
if (item.material && item.material.name == 'Petal') {
petal_2 = item
petal.geometry.morphAttributes.position.push(petal_2.geometry.attributes.position)
petal.updateMorphTargets()
}
})
})
}) 'end - gltf_2.scene.traverse'
gsap.to(params, {
value_0: 1,
duration: 4, // 持续时间
delay: 0, // 延迟时间
/* onUpdate:在动画的每一帧被调用,意味着,随着动画的进行,这个函数会不断执行。*/
onUpdate: () => {
petal.morphTargetInfluences[0] = params.value_0 // 更改,花瓣的权重
stem.morphTargetInfluences[0] = params.value_0 // 更改,茎的权重
},
// onComplete:动画完成时调用,在这个函数中,启动了另一个gsap动画
onComplete: () => {
gsap.to(params, {
value_1: 1,
duration: 4,
delay: 1, // 延迟时间
onUpdate: function () {
stem.morphTargetInfluences[1] = params.value_1
petal.morphTargetInfluences[1] = params.value_1
}
})
}
}) 'end - gsap'
})
scene.add(gltf_1.scene)
})
//#region --------------------- 看看用到的这几个gltf模型长什么样 ---------------------
// 短的,茎,花苞
gltfLoader.load('../public/assets/model-19/f4.glb', gltf => {
// console.log('gltf=', gltf)
gltf.scene.position.x = 10
gltf.scene.rotation.x = Math.PI
scene.add(gltf.scene)
})
// 长的,茎,花苞
gltfLoader.load('../public/assets/model-19/f2.glb', gltf => {
// console.log('gltf=', gltf)
gltf.scene.position.x = 20
gltf.scene.rotation.x = Math.PI
scene.add(gltf.scene)
})
// 长的茎,开的花
gltfLoader.load('../public/assets/model-19/f1.glb', gltf => {
// console.log('gltf=', gltf)
gltf.scene.position.x = 30
gltf.scene.rotation.x = Math.PI
scene.add(gltf.scene)
})
//#endregion --------------------- 看看用到的这几个gltf模型长什么样 ---------------------
//#region renderer
const renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true // 启用对数深度缓冲区,有助于减少在深度测试中常见的z-fighting问题,特别是在处理远距离物体时
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true // 阴影
renderer.physicallyCorrectLights = true // 物理光照效果
// 设置电影渲染模式
renderer.toneMapping = THREE.ACESFilmicToneMapping
`
设置,渲染器输出的颜色编码方式为:sRGB方式,
sRGB编码:一种非线性的颜色空间,更接近于人眼对亮度的感知方式,更加准确和自然。`
renderer.outputEncoding = THREE.sRGBEncoding
`
禁用自动清除,每次渲染调用之前,渲染器不会自动清除其颜色、深度、模板缓冲区,
这在,想要在某些帧上,保留前一帧的内容时非常有用,
如:实现某些类型的累积效果,或自定义渲染流程。`
renderer.autoClear = false
`
渲染器的对象排序功能:
渲染器会根据,对象到摄像机的距离,来排序场景中的对象,并且,先渲染较远的对象,再渲染较近的对象。
有助于减少由于深度测试引起的渲染问题,如z-fighting(深度冲突),
并且,可能提高渲染性能,因为,早期深度测试,可以更早地拒绝被遮挡的像素。
缺点:
对象排序,可能会增加渲染前的处理时间,可能不适用于所有场景,
特别是,当场景中有大量透明对象,或复杂的层叠关系时,简单的距离排序可能不足以解决问题,甚至可能引入新的问题。`
renderer.sortObjects = true
`
将webgl渲染的canvas内容添加到body`
document.body.appendChild(renderer.domElement)
//#endregion
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
function render() {
controls.update()
renderer.render(scene, camera)
requestAnimationFrame(render)
}
render()
window.addEventListener('resize', () => {
// 重置相机的宽高比
camera.aspect = window.innerWidth / window.innerHeight
// 更新相机的投影矩阵
camera.updateProjectionMatrix()
// 重置渲染器的宽高比
renderer.setSize(window.innerWidth, window.innerHeight)
// 更新渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio)
})
3.js - 变形动画 - 优化
于 2024-09-27 17:34:51 首次发布