-
Environment Map
- as a background
- as reflection
- as lighting
-
Set up
<script setup>
import * as THREE from 'three'
import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
import GUI from 'lil-gui'
/**
* scene
*/
const scene = new THREE.Scene()
/**
* torus knot
*/
const torusKnot = new THREE.Mesh(
new THREE.TorusKnotGeometry(1, 0.4, 100, 16),
new THREE.MeshBasicMaterial()
)
torusKnot.position.y = 4
scene.add(torusKnot)
/**
* camera
*/
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
100)
camera.position.set(4, 5, 4)
scene.add(camera)
/**
* renderer
*/
const renderer = new THREE.WebGLRenderer({})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
document.body.appendChild(renderer.domElement)
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
/**
* control
*/
const controls = new OrbitControls(camera, renderer.domElement)
controls.target.y = 3.5
controls.enableDamping = true
/**
* tick
*/
const tick = () => {
controls.update()
renderer.render(scene, camera)
requestAnimationFrame(tick)
}
tick()
/**
* gui
*/
const gui = new GUI()
</script>
-
Model
- 添加model后向下移动camera就能看见了
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; /** * loaders */ const gltfLoader = new GLTFLoader() ... ... /** * models */ gltfLoader.load( '../public/models/environment-map/models/FlightHelmet/glTF/FlightHelmet.gltf', (gltf) => { // console.log((gltf)); scene.add(gltf.scene) } )
- the model is too small and it's black, because its materials are
MeshStandardMaterial
and those need light - cube texture environment map, load the textures in this order: positive x, negative x, positive y, negative y, positive z, negative z
/** * loaders */ ... const cubeTextureLoader = new THREE.CubeTextureLoader() /** * environment map */ // LDR cube texture const environmentMap = cubeTextureLoader.load([ '../public/models/environment-map/environmentMaps/0/px.png', '../public/models/environment-map/environmentMaps/0/nx.png', '../public/models/environment-map/environmentMaps/0/py.png', '../public/models/environment-map/environmentMaps/0/ny.png', '../public/models/environment-map/environmentMaps/0/pz.png', '../public/models/environment-map/environmentMaps/0/nz.png' ]) scene.background = environmentMap
- move the torus knot to the side and change its material to
MeshStandardMaterial
/** * torus knot */ const torusKnot = new THREE.Mesh( new THREE.TorusKnotGeometry(1, 0.4, 100, 16), new THREE.MeshStandardMaterial({ roughness: 0.3, metalness: 1, color: 0xaaaaaa }) ) torusKnot.position.x = -4 ...
- use the environment map to light up the model, 也可以在单个model的material上设置
envMap
属性,但我们import的model层级结构比较复杂,不方便遍历添加,所以我们使用以下方法
/** * environment map */ // LDR cube texture ... scene.environment = environmentMap // to the hold scene scene.background = environmentMap
- control the environment map's intensity, it has to be done on each material, we're going to touch the hole scene using
traverse()
, this method is available on object3D, we'll do it in a separated function and call it once is loaded - we only want to apply the environment map to the
Meshes
that have aMeshStandardMaterial
/** * update all materials */ const updateAllMaterials = () => { scene.traverse(child => { // console.log(child) // mesh, group, perspectiveCamera... // child instanceof THREE.Mesh && child.material instanceof THREE.MeshStandardMaterial if(child.isMesh && child.material.isMeshStandardMaterial) { // console.log(child); child.material.envMapIntensity = 3 // 增强环境贴图的反射强度 } }) }
/** * models */ gltfLoader.load( '../public/models/environment-map/models/FlightHelmet/glTF/FlightHelmet.gltf', (gltf) => { ... updateAllMaterials() } )
- control the
envMapIntensity
by using theDat.gui
/** * gui */ const gui = new GUI() const global = { envMapIntensity: 1 } gui .add(global, 'envMapIntensity') .min(0) .max(10) .step(0.001) .onChange(updateAllMaterials)
/** * update all materials */ const updateAllMaterials = () => { scene.traverse(child => { ... child.material.envMapIntensity = global.envMapIntensity // 调整环境贴图的反射强度 } }) }
-
Background blurriness and intensity 模糊度、强度
scene.backgroundBlurriness = 0
scene.backgroundIntensity = 1
gui
.add(scene, 'backgroundBlurriness')
.min(0)
.max(1)
.step(0.001)
gui
.add(scene, 'backgroundIntensity')
.min(0)
.max(10)
.step(0.001)
-
HDRI - Equirectangular Environment Map 等距矩形环境贴图
-
HDR Files: File extension is
.hdr
, high dynamic range 高动态范围 - HDRI: color values stored have a much higher range than a traditional image
- Equirectangular: only one picture containing kind of a 360° view of the surrounding
- comment the environment map(keep the
backgroundBlurriness
andbackgroundIntensity
)
/** * environment map */ // LDR cube texture // const environmentMap = cubeTextureLoader.load([ // '../public/models/environment-map/environmentMaps/0/px.png', // '../public/models/environment-map/environmentMaps/0/nx.png', // '../public/models/environment-map/environmentMaps/0/py.png', // '../public/models/environment-map/environmentMaps/0/ny.png', // '../public/models/environment-map/environmentMaps/0/pz.png', // '../public/models/environment-map/environmentMaps/0/nz.png' // ]) // scene.environment = environmentMap // to the hold scene // scene.background = environmentMap scene.backgroundBlurriness = 0 scene.backgroundIntensity = 1
- load and use the HDRI, we need to use the RGBELoader, RGBE stands for Red Blue Green Exponent, the exponent means stores the brightness, 使用
RGBELoader
加载的背景看起来要更亮,因为存储在文件中的值更多、范围更大
import {RGBELoader} from 'three/addons/loaders/RGBELoader.js' /** * loaders */ ... const rgbeLoader = new RGBELoader()
/** * environment map */ ... ... // HDR (RGBE) equirectangular rgbeLoader.load('../public/models/environment-map/environmentMaps/0/2k.hdr', (environmentMap) => { environmentMap.mapping = THREE.EquirectangularReflectionMapping scene.environment = environmentMap scene.background = environmentMap }) scene.backgroundBlurriness = 0 scene.backgroundIntensity = 1
-
这里我们展示一些通过blender创建的不同类型的HDR背景图所呈现的效果
-
HDR Files: File extension is
-
AI generated environment using NVIDIA Canvas
- the software is in beta and only works on windows
- the extension is
.exr
, our exported file is also an HDR, but EXR can also store layer - import the
EXRLoader
...
import { EXRLoader } from 'three/addons/loaders/EXRLoader.js';
/**
* loaders
*/
...
const exrLoader = new EXRLoader()
// HDR (RGBE) equirectangular
// rgbeLoader.load('../public/environment-map/environmentMaps/blender-2k-1.hdr', environmentMap => {
// environmentMap.mapping = THREE.EquirectangularReflectionMapping
// scene.environment = environmentMap
// scene.background = environmentMap
// })
// HDR (EXR) equirectangular
exrLoader.load('../public/environment-map/environmentMaps/nvidia-canvas-4k.exr', environmentMap => {
environmentMap.mapping = THREE.EquirectangularReflectionMapping
scene.environment = environmentMap
scene.background = environmentMap
})
-
AI generated environment map using BlockadeLabs
/**
* loaders
*/
...
const textureLoader = new THREE.TextureLoader()
// HDR (RGBE) equirectangular
// rgbeLoader.load('../public/environment-map/environmentMaps/blender-2k-1.hdr', environmentMap => {
// environmentMap.mapping = THREE.EquirectangularReflectionMapping
// scene.environment = environmentMap
// scene.background = environmentMap
// })
// HDR (EXR) equirectangular
// exrLoader.load('../public/environment-map/environmentMaps/nvidia-canvas-4k.exr', environmentMap => {
// environmentMap.mapping = THREE.EquirectangularReflectionMapping
// scene.environment = environmentMap
// scene.background = environmentMap
// })
// scene.backgroundBlurriness = 0
// scene.backgroundIntensity = 1
// LDR equirectangular
const environmentMap = textureLoader.load('../public/environment-map/environmentMaps/blockadesLabsSkybox/anime_art_style_japan_streets_with_cherry_blossom_.jpg')
environmentMap.mapping = THREE.EquirectangularReflectionMapping
environmentMap.colorSpace = THREE.SRGBColorSpace
scene.environment = environmentMap
scene.background = environmentMap
-
Ground Projected Environment Map
- when using the environment map as background, the objects look like are flying
- to use on of the HDR environment maps, only as the
environment
-
RealTime EnvironmentMap
/** * real time environment map */ const environmentMap = textureLoader.load('../public/environment-map/environmentMaps/blockadesLabsSkybox/interior_views_cozy_wood_cabin_with_cauldron_and_p.jpg') environmentMap.mapping = THREE.EquirectangularReflectionMapping environmentMap.colorSpace = THREE.SRGBColorSpace scene.background = environmentMap
- we're going to create a torus or a donut surrounding the scene and try to make the donut illuminate and reflect on the surface of our objects
/** * holy donut */ const holyDonut = new THREE.Mesh( new THREE.TorusGeometry(8, 0.5), new THREE.MeshBasicMaterial({color: 'white'}) ) holyDonut.position.y = 3.5 scene.add(holyDonut)
- we're going to render the scene inside our own environment map texture and it's going to be a cube texture
- 创建立方体纹理:we need to use WebGLCubeRenderTarget, we need to create textures on each frame
/** * cube render target */ const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256, // 分辨率 { type: THREE.HalfFloatType } ) scene.environment = cubeRenderTarget.texture
- we need to use CubeCamera, because we need to render 6 texture for each face
// cube camera const cubeCamera = new THREE.CubeCamera(0.1, 100, cubeRenderTarget) // near, far
/** * tick */ const clock = new THREE.Clock() const tick = () => { const elapsedTime = clock.getElapsedTime() if(holyDonut) { holyDonut.rotation.x = Math.sin(elapsedTime) * 2 cubeCamera.update(renderer, scene) } controls.update() renderer.render(scene, camera) requestAnimationFrame(tick) } tick()
- we can make the cube color go beyond the 0 to 1 range
/** * holy donut */ const holyDonut = new THREE.Mesh( ... new THREE.MeshBasicMaterial({color: new THREE.Color(10, 4, 2)}) ) ...