【Three.js基础学习】21.Realistic rendering
前言
课程回顾
渲染器
1.色调映射
值意在将高动态范围](HDR)值转换为低动态范围(LDR)
Three.is中的色调映射实际上会伪造将LDR转换为HDR的过程,即使颜色不是HDR,
结果会产生非常逼真的渲染效果
THREE .NoToneMapping (default) 无色调映射
THREE.LinearToneMapping 线性色调映射
THREE.ReinhardToneMapping
THREE.CineonToneMapping
THREE.ACESFilmicToneMapping
2.色调映射曝光
放了多少光
toneMappingExposure
3.抗锯齿 Antialiasing(混叠) 取决于像素比
讨论两种方法
1.超级采样(SSAA)或全屏采样(FSAA)
我们把分辨率提高到实际分辨率以外。当调整为正常尺寸时,每个像素颜色将自动从渲染的4个像素中平均出来
简单但对性能不好
2.多采样(MSAA)
由最新的GPU自动执行
将检查被渲染像素的邻居。如果是几何图形的边缘,将将其颜色与邻居的颜色混合。
仅适用于几何边缘
平行光投射阴影
环境地图不能投射阴影我们需要添加一盏与环境图的照明大致相符的光,并用它投射阴影
阴影
开启
renderer.shadowMap.enabled = true ; // 开启阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap // 设置阴影类型
设置光辅助相机
平行光开启阴影-》设置相机更行矩阵
updateWorldMatrix
在物体模型上开启阴影 ,并且接受阴影
纹理和颜色空间
使用木橱柜磨损长度/纹理在模型下方创建一个8x8平面
使用castle brick broken 06/纹理在模型后面创建一个8x8的平面
纹理看起来奇怪的白色,这是由于“颜色空间”颜色空间是一种根据人眼敏感度优化颜色存储方式的方法。
主要涉及应该被看到的纹理(在我们的例子中,bricksColorTexture)
需要改变颜色空间 , 哪些图gl,arm属于线性,diff属于sRGB ,需要变成线性
为什么不对模型使用颜色空间?
原因是GLTF已经包含这些颜色信息
在颜色纹理上更改颜色空间
floorColorTexture.colorSpace = THREE.SRGBColorSpace
换成汉堡包
降低环境强度发现很奇怪,有线的条纹
原因:由于计算时精确原因,阴影痤疮可能发生在光滑,平坦的表面上;表面是否在阴影中 ;汉堡包正在给自己蒙上一层阴影
解决:1. 在平行光上,正常量偏移,让汉堡包变大或变小,这样表面没有深度,不会接受阴影
bias:
normalBias:
贴图强度会降低阴影的可见性
一、代码
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import GUI from 'lil-gui'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
import { floor } from 'three/examples/jsm/nodes/nodes'
/**
* Loaders
*/
const gltfLoader = new GLTFLoader()
const rgbeLoader = new RGBELoader()
/*
TextureLoad
*/
const textureLoader = new THREE.TextureLoader()
/**
* Base
*/
// Debug
const gui = new GUI()
const global = {}
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
/**
* Update all materials
*/
const updateAllMaterials = () =>
{
scene.traverse((child) =>
{
if(child.isMesh && child.material.isMeshStandardMaterial)
{
child.material.envMapIntensity = global.envMapIntensity
child.castShadow = true // 在物体上开启阴影
child.receiveShadow = true
}
})
}
/**
* Environment map
*/
// Global intensity
global.envMapIntensity = 1
gui
.add(global, 'envMapIntensity')
.min(0)
.max(10)
.step(0.001)
.onChange(updateAllMaterials)
// HDR (RGBE) equirectangular
rgbeLoader.load('/environmentMaps/0/2k.hdr', (environmentMap) =>
{
environmentMap.mapping = THREE.EquirectangularReflectionMapping
scene.background = environmentMap
scene.environment = environmentMap
})
/*
Directional Light
*/
const directionalLight = new THREE.DirectionalLight('#ffffff',2)
directionalLight.position.set(-4,6.5,2.5)
scene.add(directionalLight)
gui.add(directionalLight,'intensity').min(0).max(10).step(0.001).name('lightIntensity') // 光强度
gui.add(directionalLight.position,'x').min(-10).max(10).step(0.001).name('lightX') // 光x轴
gui.add(directionalLight.position,'y').min(-10).max(10).step(0.001).name('lightY')
gui.add(directionalLight.position,'z').min(-10).max(10).step(0.001).name('lightZ')
// Shadows
directionalLight.castShadow = true
directionalLight.shadow.camera.far = 15
directionalLight.shadow.normalBias = 0.027
directionalLight.shadow.bias = -0.004
directionalLight.shadow.mapSize.set(512,512) // 数值越大 阴影越清晰
gui.add(directionalLight,'castShadow')
gui.add(directionalLight.shadow,'normalBias').min(-0.05).max(0.05).step(0.001)
gui.add(directionalLight.shadow,'bias').min(-0.05).max(0.05).step(0.001)
// Helper 光辅助相机
// const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
// scene.add(directionalLightCameraHelper)
// target
directionalLight.target.position.set(0,4,0) //若只是设置这个不生效 ,
directionalLight.target.updateWorldMatrix() // 加上这个 渲染时,更新矩阵模型 就可以
/**
* Models
*/
// Helmet
// gltfLoader.load(
// '/models/FlightHelmet/glTF/FlightHelmet.gltf',
// (gltf) =>
// {
// gltf.scene.scale.set(10, 10, 10)
// scene.add(gltf.scene)
// updateAllMaterials()
// }
// )
// 汉堡包
gltfLoader.load(
'/models/hamburger.glb',
(gltf) =>
{
gltf.scene.scale.set(0.4, 0.4, 0.4)
gltf.scene.position.set(0,2.5,0)
scene.add(gltf.scene)
updateAllMaterials()
}
)
/*
floor
*/
const floorColorTexture = textureLoader.load('/textures/wood_cabinet_worn_long/wood_cabinet_worn_long_diff_1k.jpg')
const floorColorNormalTexture = textureLoader.load('/textures/wood_cabinet_worn_long/wood_cabinet_worn_long_nor_gl_1k.jpg')
const floorColorAORoughnessMetalnessTexture = textureLoader.load('/textures/wood_cabinet_worn_long/wood_cabinet_worn_long_arm_1k.jpg')
floorColorTexture.colorSpace = THREE.SRGBColorSpace
const floorPlaneInWooden = new THREE.Mesh(
new THREE.PlaneGeometry(8,8),
new THREE.MeshStandardMaterial({
map:floorColorTexture,
normalMap:floorColorNormalTexture, // 法线贴图 ,保持细节
aoMap:floorColorAORoughnessMetalnessTexture, // AO贴图,金属度和粗糙度
roughnessMap:floorColorAORoughnessMetalnessTexture,
metalnessMap:floorColorAORoughnessMetalnessTexture,
// color:new THREE.Color(10, 4, 2)
})
)
floorPlaneInWooden.rotation.x = - Math.PI * 0.5
scene.add(floorPlaneInWooden)
/*
wall
*/
const wallColorTexture = textureLoader.load('/textures/castle_brick_broken_06/castle_brick_broken_06_diff_1k.jpg')
const wallColorNormalTexture = textureLoader.load('/textures/castle_brick_broken_06/castle_brick_broken_06_nor_gl_1k.jpg')
const wallColorAORoughnessMetalnessTexture = textureLoader.load('/textures/castle_brick_broken_06/castle_brick_broken_06_arm_1k.jpg')
wallColorTexture.colorSpace = THREE.SRGBColorSpace
const wallPlaneInWooden = new THREE.Mesh(
new THREE.PlaneGeometry(8,8),
new THREE.MeshStandardMaterial({
map:wallColorTexture,
normalMap:wallColorNormalTexture, // 法线贴图 ,保持细节
aoMap:wallColorAORoughnessMetalnessTexture, // AO贴图,金属度和粗糙度
roughnessMap:wallColorAORoughnessMetalnessTexture,
metalnessMap:wallColorAORoughnessMetalnessTexture,
// color:new THREE.Color(10, 4, 2)
})
)
// wallPlaneInWooden.rotation.x = - Math.PI * 0.5
wallPlaneInWooden.position.y = 4
wallPlaneInWooden.position.z = - 4
scene.add(wallPlaneInWooden)
/**
* Sizes
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
window.addEventListener('resize', () =>
{
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
/**
* Camera
*/
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.set(4, 5, 4)
scene.add(camera)
// Controls
const controls = new OrbitControls(camera, canvas)
controls.target.y = 3.5
controls.enableDamping = true
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias:true // 反别名
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
// tone mapping
renderer.toneMapping = THREE.ReinhardToneMapping
renderer.toneMappingExposure = 3
gui.add(renderer,'toneMapping',{
No:THREE.NoToneMapping,
Linear:THREE.LinearToneMapping,
Reinhard:THREE.ReinhardToneMapping,
Cineon:THREE.CineonToneMapping,
ACESFimlmic:THREE.ACESFilmicToneMapping,
})
gui.add(renderer,'toneMappingExposure').min(0).max(10).step(0.001)
// Shadows
renderer.shadowMap.enabled = true ; // 开启阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap // 设置阴影类型
/**
* Animate
*/
const tick = () =>
{
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
二、知识点
1.色调映射,抗锯齿
可以看的在颜色变化的周围,没有想锯齿一样的,而是很平滑
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias:true // 反别名
})
色调映射:值意在将高动态范围](HDR)值转换为低动态范围(LDR)
THREE .NoToneMapping (default) 无色调映射
THREE.LinearToneMapping 线性色调映射
THREE.ReinhardToneMapping
THREE.CineonToneMapping
THREE.ACESFilmicToneMapping
色调映射曝光
放了多少光
toneMappingExposure
抗锯齿 Antialiasing(混叠) 取决于像素比
讨论两种方法
1.超级采样(SSAA)或全屏采样(FSAA)
我们把分辨率提高到实际分辨率以外。当调整为正常尺寸时,每个像素颜色将自动从渲染的4个像素中平均出来
简单但对性能不好
2.多采样(MSAA) (采用这种)
由最新的GPU自动执行
将检查被渲染像素的邻居。如果是几何图形的边缘,将将其颜色与邻居的颜色混合。
仅适用于几何边缘
2. 平行光投射阴影,阴影
激活灯光投射阴影,开启阴影,物体上开启阴影接受
renderer.shadowMap.enabled = true ; // 开启阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap // 设置阴影类型
const updateAllMaterials = () =>
{
scene.traverse((child) =>
{
if(child.isMesh && child.material.isMeshStandardMaterial)
{
child.material.envMapIntensity = global.envMapIntensity
child.castShadow = true // 在物体上开启阴影
child.receiveShadow = true
}
})
}
const directionalLight = new THREE.DirectionalLight('#ffffff',2)
directionalLight.position.set(-4,6.5,2.5)
scene.add(directionalLight)
gui.add(directionalLight,'intensity').min(0).max(10).step(0.001).name('lightIntensity') // 光强度
gui.add(directionalLight.position,'x').min(-10).max(10).step(0.001).name('lightX') // 光x轴
gui.add(directionalLight.position,'y').min(-10).max(10).step(0.001).name('lightY')
gui.add(directionalLight.position,'z').min(-10).max(10).step(0.001).name('lightZ')
// Shadows
directionalLight.castShadow = true
directionalLight.shadow.camera.far = 15
directionalLight.shadow.normalBias = 0.027
directionalLight.shadow.bias = -0.004
directionalLight.shadow.mapSize.set(512,512) // 数值越大 阴影越清晰
gui.add(directionalLight,'castShadow')
3.颜色空间设置
设置之后可以看到原本黄色的墙正常
wallColorTexture.colorSpace = THREE.SRGBColorSpace
floorColorTexture.colorSpace = THREE.SRGBColorSpace
4.不同模型 阴影设置
设置之后对应条纹优化
global.envMapIntensity = 0
directionalLight.shadow.normalBias = 0.027
directionalLight.shadow.bias = -0.004
gui.add(directionalLight.shadow,'normalBias').min(-0.05).max(0.05).step(0.001)
gui.add(directionalLight.shadow,'bias').min(-0.05).max(0.05).step(0.001)
总结
让显示的效果更好!