threepipe
Version:
A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.
103 lines (91 loc) • 3.54 kB
text/typescript
import {
_testFinish,
IObject3D,
LinearToneMapping,
LoadingScreenPlugin,
Mesh,
PerspectiveCamera2,
PlaneGeometry,
PopmotionPlugin,
ProgressivePlugin, shaderReplaceString,
Texture,
ThreeViewer,
ToneMapping,
TonemapPlugin,
UnlitMaterial,
VirtualCamerasPlugin,
ShaderChunk,
} from 'threepipe'
async function init() {
const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
debug: true,
rgbm: true,
msaa: true,
plugins: [new ProgressivePlugin(16), LoadingScreenPlugin],
})
const virtualCameras = viewer.addPluginSync(VirtualCamerasPlugin)
const popmotion = viewer.addPluginSync(PopmotionPlugin)
await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
})
const aspect = 2
const plane = new Mesh(
new PlaneGeometry(5 * aspect, 5)
.translate(0, 0, -3),
new UnlitMaterial({
color: '#ffffff',
})
)
plane.castShadow = false
plane.receiveShadow = true
viewer.scene.addObject(plane)
const camera = new PerspectiveCamera2('', viewer.canvas, false, 45, aspect)
camera.position.set(0, 0, 5)
camera.target.set(0, 0.25, 0)
camera.userData.autoLookAtTarget = true
camera.near = 1
camera.far = 10
camera.setDirty()
const vCam = virtualCameras.addCamera(camera)
plane.material.map = vCam.target.texture as Texture
popmotion.animate({
from: 0,
to: 1,
repeat: Infinity,
duration: 6000,
onUpdate: (v)=>{
// Set camera position xz in a circle around the target
const angle = v * Math.PI * 2 + Math.PI / 2
const radius = 5
camera.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius)
camera.setDirty()
viewer.setDirty() // since camera is not in the scene
},
})
// We need to disable tonemapping when rendering the virtual camera, otherwise the tonemapping will be applied multiple times.
let lastTonemapping: ToneMapping = LinearToneMapping
const tonemap = viewer.getPlugin(TonemapPlugin)!
virtualCameras.addEventListener('preRenderCamera', ()=>{
lastTonemapping = tonemap.toneMapping
// Comment this and see what happens to the color in the plane
tonemap.toneMapping = LinearToneMapping
})
virtualCameras.addEventListener('postRenderCamera', ()=>{
tonemap.toneMapping = lastTonemapping
})
// (Extra optional) extension decoding rgbm render target when using rgbm
if (viewer.renderManager.rgbm) {
plane.material.registerMaterialExtensions([{
shaderExtender: (shader, material) => {
if (material.map?.colorSpace !== 'rgbm-16') return
shader.fragmentShader = shaderReplaceString(shader.fragmentShader, '#include <map_fragment>', ShaderChunk.map_fragment)
shader.fragmentShader = shaderReplaceString(shader.fragmentShader, 'texture2D( map, vMapUv )', 'RGBM16ToLinear(texture2D( map, vMapUv ))', {replaceAll: true})
},
computeCacheKey: (material) => material.map?.colorSpace === 'rgbm-16' ? 'rgbm' : '',
}])
}
}
init().finally(_testFinish)