threejs 实现3D游戏(10)—— 表面反射效果概述 真实的物理效果中,最难实现的就是真实的光照,threejs底
概述
真实的物理效果中,最难实现的就是真实的光照,threejs底层是webgl,它是openGL的一个浏览器适配版本,当然也支持光线追踪,以此实现逼真的光影效果如————反射效果。
但是最大的问题是光追通常需要大量的计算资源,我们以前介绍过,图形渲染计算需要的是短时间数亿次的并发运算。这种计算只有GPU能满足,而GPU是很昂贵的资源(特别是在其他的3D模型已经占用大量资源时)。
为了满足threejs运行的环境(浏览器),我们需要在性能和效果间权衡。
环境贴图
这是一种最简单的实现,也是对性能要求最小的实现方式,大部分时候我倾向于选择它。
通过一个立方体贴图或全景贴图来表示整个场景的环境光照,从而模拟物体表面的反射效果。Three.js 支持使用 MeshStandardMaterial
或 MeshPhysicalMaterial
配合 envMap
属性来实现环境贴图的反射效果。这种方式性能较好,但反射是静态的,不能实时反映环境的变化。
threejs中原生的写法
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 0, // 金属
roughness: 0, // 粗糙
envMap: cubeTexture,//这里放全景贴图的texture, 你也可以用cubeCamera自己捕获立方体贴图
envMapIntensity: 1,// 环境贴图的反射强度
});
上面的写法,是对单个材质的环境反射贴图的设置,有时候我们有的全景贴图是整个场景中的,不需要一个个去设置。
通过设置 scene.environment
,Three.js 自动地将这个环境贴图应用到了所有支持环境贴图的材质上,例如 MeshStandardMaterial
和 MeshPhysicalMaterial
。
react-three 中的 Environment 组件
知道了原理,在react-three中有完整的封装:Environment 组件,我们直接使用即可
import { Environment } from "@react-three/drei";
<Environment
files="./textures/HJ_360.hdr" // 此处仅仅支持hdr文件
background // 是否显示全景图为背景
environmentIntensity={1.2} // 环境的反射强度
backgroundRotation={[0, Math.PI / 2, 0]} // 背景全景图的旋转
environmentRotation={[0, Math.PI / 2, 0]} // 环境贴图的旋转
/>
需要注意的是,如果你的贴图和真实场景的角度有出入,往往需要environmentRotation 将其设置到正确的角度。
色调和曝光的设置
此外环境贴图的光照效果和反射效果还受到曝光和色调映射的影响,下面是一个设置示例。
动态效果
如果要设置更加动态的反射效果,可以考虑使用CubeCamera
来动态更新环境贴图。
CubeCamera
会拍摄当前场景并生成一个立方体贴图,useBoxProjectedEnv
可以用于控制反射贴图的位置和大小。可以通过设置frames参数控制CubeCamera的拍摄更新频率。
不过cubeCamera获取立方体贴图时,会因为相机和景物间的距离导致反射贴图失真,如下图我在场景中心靠近相机的地方放了一个小正方体,结果在反射中它特别的大。
解决方案之一是分别使用多个CubeCamera,独立拍摄不同的贴图,应用到不同的材质上。
但会有性能问题,所以这个CubeCamera动态贴图更适合在空旷的场景使用。
核心代码如下:
const projection = useBoxProjectedEnv(
[0, 0, 0], // Position
[1, 1, 1] // Scale )
<CubeCamera frames={1}> {(texture) => (
<mesh>
<planeGeometry />
<meshStandardMaterial envMap={texture} {...projection} />
</mesh> )}
</CubeCamera>
反射材质
这是在react-three/drei中在MeshStandardMaterial
基础上扩展的一种用于反射的材质: MeshReflectorMaterial
他的核心原理是,在场景中使用渲染目标(render target)来捕捉环境的反射信息,并将这些信息应用到材质上。 这种方式不依赖于静态的反射贴图,而是通过实时的渲染来生成反射效果。这允许材质动态地反映周围的环境。
这种材质的使用较为简单方便,具体的属性可以参考文档。
<mesh rotation={[Math.PI / 2, 0, 0]} geometry={geometry} >
<MeshReflectorMaterial
blur={[300, 100]}
mirror={0}
resolution={2048}
mixBlur={1}
mixStrength={80}
roughness={1}
depthScale={1.2}
minDepthThreshold={0.4}
maxDepthThreshold={1.4}
color="#151515"
metalness={0.6}
/>
</mesh>
此外还有一个已经弃用的Refactor
组件,及其示例:
Refactor组件使用示例
后处理——屏幕空间反射(SSR)
一种基于屏幕空间的后处理效果,通过计算屏幕上像素与其周围物体的距离和角度,模拟出近似的反射效果。SSR 可以实现更真实的动态反射,但对性能要求较高。
核心代码如下:
import { EffectComposer,SSR } from "@react-three/postprocessing";
export function Effects() {
return (
<EffectComposer>
<SSR />
</EffectComposer>
);
}
总结
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
反射贴图 (Reflection Mapping) | 性能高、实现简单、兼容性强 | 不适合动态场景、反射效果不准确 | 静态场景,静止物体表面反射效果 |
反射材质 (Reflective Materials) | 动态反射、细节丰富 | 性能开销大、分辨率受限 | 动态反射场景,如水面、镜子反射效果 |
屏幕空间反射 (SSR) | 真实感强、动态更新、支持复杂几何体 | 只限于屏幕空间、性能消耗高、可能产生伪影 | 需要高保真动态反射的场景,如游戏中的水面或玻璃 |
结论
- 反射贴图 是性能最佳的选择,适用于静态、环境固定的场景。
- 反射材质 适用于需要动态反射的简单场景,适度的计算复杂度和适应性使其成为动态环境反射的可靠选择。
- SSR 是最逼真的方法,适用于高端硬件和需要高保真反射的场景,但要考虑性能开销和可能的视觉伪影问题。
转载自:https://juejin.cn/post/7410340504477614107