一個(gè)神庫(kù)!在 Vue3 中實(shí)現(xiàn) "大氣散射" 特效!
哈嘍,大家好!今天我得給大家分享一個(gè)超厲害的技巧,在 Vue3 里實(shí)現(xiàn)超炫的大氣層渲染、大氣散射特效!
這效果一出來(lái),直接讓咱們的 3D 場(chǎng)景逼格滿滿,就跟打開(kāi)了新世界的大門(mén)一樣。
先給大家介紹個(gè)神器——@takram/three-atmosphere。
這玩意兒是個(gè)專(zhuān)門(mén)搞大氣散射效果的庫(kù),簡(jiǎn)單來(lái)說(shuō),就是能把天空那種光影變化、太陽(yáng)月亮的光照方向都模擬出來(lái),讓咱們的 3D 場(chǎng)景看起來(lái)就跟真的一樣。
探秘 @takram/three-atmosphere
@takram/three-atmosphere 是基于 Three.js 與 R3F(React Three Fiber)精心打造的預(yù)計(jì)算大氣散射實(shí)現(xiàn)庫(kù),它源于 Eric Bruneton 的深厚理論,專(zhuān)為 Web GIS 引擎渲染原型而生。
其本質(zhì)是模擬大氣對(duì)光線的散射,涵蓋了太陽(yáng)和月亮的光照方向、天空顏色漸變以及云層等自然現(xiàn)象,讓 3D 場(chǎng)景得以沉浸在逼真的天空之下。
主要特性
- 預(yù)計(jì)算大氣散射 :可模擬大氣的光照和散射效果,適用于大規(guī)模場(chǎng)景的天空渲染和光影效果模擬。
- 支持多種光照模式 :提供延遲光照和前向光照兩種模式。延遲光照適合大規(guī)模場(chǎng)景,但僅支持 Lambertian BRDF;前向光照與 Three.js 內(nèi)置材質(zhì)和陰影兼容,但更適合小規(guī)模場(chǎng)景。
- 豐富的組件和功能 :包含 Atmosphere、Sky、Stars、SkyLight、SunLight、AerialPerspective 等 R3F 組件,以及 AtmosphereMaterialBase、SkyMaterial、StarsMaterial、SkyLightProbe、SunDirectionalLight、AerialPerspectiveEffect 等 Three.js 材質(zhì)和效果類(lèi)。
接下來(lái),咱們看看在 Vue3 里怎么用這個(gè)神器。
安裝
第一步,得先把庫(kù)裝上。在項(xiàng)目里打開(kāi)終端,敲上
npm install @takram/three-atmosphere three postprocessing`
把 @takram/three-atmosphere、three 和 postprocessing 這幾個(gè)庫(kù)都裝好。
創(chuàng)建 Vue 組件
接著,新建個(gè) Vue 組件,比如叫 AtmosphereDemo.vue。
搭建基本結(jié)構(gòu)
先寫(xiě)個(gè)最簡(jiǎn)單的模板,給咱們的 3D 場(chǎng)景留個(gè)位置。
<template>
<div ref="containerRef" class="container"></div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const containerRef = ref(null)
onMounted(() => {
// 后面的代碼都放這兒
})
</script>
<style scoped>
.container {
width: 100%;
height: 100vh;
}
</style>
初始化場(chǎng)景、相機(jī)和渲染器
在 onMounted 里,先用 Three.js 的基本元素把一個(gè)簡(jiǎn)單的 3D 空間搭起來(lái)。
const container = containerRef.value
const width = container.clientWidth
const height = container.clientHeight
// 創(chuàng)建場(chǎng)景
const scene = new THREE.Scene()
// 創(chuàng)建相機(jī)
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
camera.position.z = 10
// 創(chuàng)建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
container.appendChild(renderer.domElement)
這就相當(dāng)于給咱們的 3D 場(chǎng)景搭建好了 “舞臺(tái)”,有了展示的空間。
加載預(yù)計(jì)算紋理
然后,用 PrecomputedTexturesLoader 把預(yù)計(jì)算紋理加載進(jìn)來(lái),這玩意兒可是讓場(chǎng)景真實(shí)的關(guān)鍵。這紋理文件就像是給天空加上真實(shí)光影的貼圖。
import { PrecomputedTexturesLoader } from '@takram/three-atmosphere'
const precomputedTexturesLoader = new PrecomputedTexturesLoader()
precomputedTexturesLoader.load(
'/assets', // 紋理目錄路徑,需根據(jù)實(shí)際項(xiàng)目結(jié)構(gòu)設(shè)置
(progress) => {
console.log('Loading:', progress)
}
)
創(chuàng)建大氣層效果
接下來(lái),把 Atmosphere 實(shí)例化,開(kāi)啟大氣效果,這就相當(dāng)于把整個(gè)天空的光影變化效果給啟動(dòng)起來(lái)。
import { Atmosphere } from '@takram/three-atmosphere/three'
const atmosphere = new Atmosphere()
atmosphere.date = new Date()
添加天空組件
再把 Sky、SkyLight、SunLight 這些組件加進(jìn)去,分別模擬天空、天空光照和太陽(yáng)光照。這就讓咱們的場(chǎng)景有了天空的顏色和光照。
import { Sky, SkyLight, SunLight } from'@takram/three-atmosphere/three'
// 添加天空
const sky = new Sky()
sky.frustumCulled = false
scene.add(sky)
// 添加天空光照
const skyLight = new SkyLight()
skyLight.position.set(0, 0, 0)
scene.add(skyLight)
// 添加太陽(yáng)光照
const sunLight = new SunLight()
sunLight.target.position.set(0, 0, 0)
scene.add(sunLight)
scene.add(sunLight.target)
創(chuàng)建渲染通道
最后,搞個(gè)渲染通道,用 EffectComposer 把 AerialPerspectiveEffect 和 ToneMappingEffect 都加上,這倆組合起來(lái),畫(huà)面效果直接拉滿。
import { AerialPerspectiveEffect } from'@takram/three-atmosphere/three'
const aerialPerspective = new AerialPerspectiveEffect(camera)
const composer = new THREE.EffectComposer(renderer, {
frameBufferType: THREE.HalfFloatType
})
composer.addPass(new THREE.RenderPass(scene, camera))
composer.addPass(
new THREE.EffectPass(
camera,
aerialPerspective,
new THREE.ToneMappingEffect({ mode: THREE.AGXMode })
)
)
設(shè)置窗口大小調(diào)整和動(dòng)畫(huà)循環(huán)
為了讓畫(huà)面能隨著窗口大小調(diào)整,并且能動(dòng)態(tài)更新,再加上窗口大小調(diào)整事件監(jiān)聽(tīng)和動(dòng)畫(huà)循環(huán)。
// 窗口大小調(diào)整事件監(jiān)聽(tīng)
window.addEventListener('resize', () => {
camera.aspect = container.clientWidth / container.clientHeight
camera.updateProjectionMatrix()
renderer.setSize(container.clientWidth, container.clientHeight)
})
// 動(dòng)畫(huà)循環(huán)
function animate() {
requestAnimationFrame(animate)
const date = newDate()
atmosphere.updateByDate(date)
renderer.render(scene, camera)
}
animate()
功能與限制
最后,給大家講講這玩意兒的功能和限制。它的功能很強(qiáng)大,能渲染天空、星星,計(jì)算天空和太陽(yáng)的光照效果,還能通過(guò)日期更新太陽(yáng)和月亮的方向。
不過(guò)呢,它也有點(diǎn)小限制,比如參考系固定為 ECEF 且不可配置,視角僅支持在大氣內(nèi)層球體上方,大氣透視的 inscatter 項(xiàng)存在地平線偽影問(wèn)題,未實(shí)現(xiàn)體積光束效果,主要針對(duì)地球大氣設(shè)計(jì),對(duì)于其他行星的渲染有限制,目前基于 GLSL 實(shí)現(xiàn),尚未支持 node-based TSL 和 WebGPU。
總之,這個(gè) @takram/three-atmosphere 真的是個(gè)超棒的工具,有了它,在 Vue3 里實(shí)現(xiàn)超炫的大氣層渲染特效就不再是夢(mèng)啦!大家趕緊試試吧,肯定能讓你的項(xiàng)目靚到起飛!
- 更多參考案例:https://takram-design-engineering.github.io/three-geospatial/?path=/story/atmosphere-minimal-setup--minimal-setup
- Github 地址:https://github.com/takram-design-engineering/three-geospatial