@egjs/view3d
Version:
Fast & Customizable glTF 3D model viewer, packed with full of features!
165 lines (133 loc) • 4.5 kB
text/typescript
import * as THREE from "three";
import { LightProbeGenerator } from "three/examples/jsm/lights/LightProbeGenerator";
import View3D from "../View3D";
/**
* Skybox texture generator
*/
class Skybox {
public static createDefaultEnv(renderer: THREE.WebGLRenderer) {
const envScene = new THREE.Scene();
const point = new THREE.PointLight(0xffffff, 0.8, 20);
point.decay = 2;
point.position.set(0, 7, 0);
envScene.add(point);
const boxGeo = new THREE.BoxBufferGeometry(1, 1, 1);
const boxMat = new THREE.MeshStandardMaterial({
side: THREE.BackSide
});
const box = new THREE.Mesh(boxGeo, boxMat);
box.castShadow = false;
box.scale.set(15, 45, 15);
box.position.set(0, 20, 0);
envScene.add(box);
const topLight = Skybox._createRectAreaLightSource({
intensity: 4.5,
width: 4,
height: 4
});
topLight.position.set(0, 2.5, 0);
topLight.rotateX(Math.PI / 2);
const frontLightIntensity = 3;
const frontLight0 = Skybox._createRectAreaLightSource({
intensity: frontLightIntensity,
width: 2,
height: 2
});
frontLight0.position.set(0, 1, 4);
frontLight0.lookAt(0, 0, 0);
const frontLight1 = Skybox._createRectAreaLightSource({
intensity: frontLightIntensity,
width: 2,
height: 2
});
frontLight1.position.set(-4, 1, 1);
frontLight1.lookAt(0, 0, 0);
const frontLight2 = Skybox._createRectAreaLightSource({
intensity: frontLightIntensity,
width: 2,
height: 2
});
frontLight2.position.set(4, 1, 1);
frontLight2.lookAt(0, 0, 0);
const backLight1 = Skybox._createRectAreaLightSource({
intensity: 2.5,
width: 2,
height: 2
});
backLight1.position.set(1.5, 1, -4);
backLight1.lookAt(0, 0, 0);
const backLight2 = Skybox._createRectAreaLightSource({
intensity: 2.5,
width: 2,
height: 2
});
backLight2.position.set(-1.5, 1, -4);
backLight2.lookAt(0, 0, 0);
envScene.add(
topLight,
frontLight0,
frontLight1,
frontLight2,
backLight1,
backLight2
);
const outputEncoding = renderer.outputEncoding;
const toneMapping = renderer.toneMapping;
renderer.outputEncoding = THREE.LinearEncoding;
renderer.toneMapping = THREE.NoToneMapping;
const renderTarget = new THREE.PMREMGenerator(renderer).fromScene(envScene, 0.035);
renderer.outputEncoding = outputEncoding;
renderer.toneMapping = toneMapping;
return renderTarget.texture;
}
/**
* Create blurred cubemap texture of the given texture and use that as the skybox
* @param {THREE.Texture} texture Equirect texture
* @returns {this}
*/
public static createBlurredHDR(view3D: View3D, texture: THREE.Texture) {
const threeRenderer = view3D.renderer.threeRenderer;
const bgScene = new THREE.Scene();
bgScene.background = texture;
// To prevent exposure applied twice
const origExposure = threeRenderer.toneMappingExposure;
threeRenderer.toneMappingExposure = 1;
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256, {
encoding: THREE.sRGBEncoding,
format: THREE.RGBAFormat
});
const cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);
cubeCamera.update(threeRenderer, bgScene);
const lightProbe = LightProbeGenerator.fromCubeRenderTarget(threeRenderer, cubeRenderTarget);
const skyboxMat = new THREE.MeshStandardMaterial({
side: THREE.BackSide
});
const geometry = new THREE.IcosahedronBufferGeometry(1, 4);
const skyboxScene = new THREE.Scene();
const skyboxMesh = new THREE.Mesh(geometry, skyboxMat);
const normals = geometry.getAttribute("normal");
for (let i = 0; i < normals.count; i++) {
normals.setXYZ(i, -normals.getX(i), -normals.getY(i), -normals.getZ(i));
}
skyboxScene.add(skyboxMesh);
skyboxScene.add(lightProbe);
cubeCamera.update(threeRenderer, skyboxScene);
threeRenderer.toneMappingExposure = origExposure;
return cubeRenderTarget.texture;
}
private static _createRectAreaLightSource({
intensity,
width,
height
}: {
intensity: number;
width: number;
height: number;
}) {
const planeBufferGeo = new THREE.PlaneBufferGeometry(width, height);
const mat = new THREE.MeshBasicMaterial();
mat.color.setScalar(intensity);
return new THREE.Mesh(planeBufferGeo, mat);
}
}
export default Skybox;