采用 WebGL 和 ThreeJS 运行实时焦散运算,需要一点相关基础。本文主要介绍焦散的原理以及计算方法
原作者
https://github.com/martinRenougithub.com代码和原文
https://github.com/martinRenou/threejs-causticsgithub.com由于本人的笔电还在维修中所以带中文注释的代码要后续补上了
什么是焦散 caustics
焦散是光从表面(在我们的情况下是空气/水界面)折射和反射时出现的光的模式。
由于在水波上发生反射和折射,水起了动态放大镜的作用,形成了这些光的图案。
本文中主要讨论由光的折射引起的焦散,主要是水下发生的事情。
为了能够得到稳定的 60 帧,使用 GLSL 编写的 shaders 在 GPU 上计算。
为了计算焦散,我们需要:
• 计算水面的折射光线(GLSL 中有内置函数)
• 求交算法,计算光线照射到环境的位置
• 通过检查光线会聚的位置来计算焦散强度
创建环境图
当涉及动态阴影计算时,一种众所周知的技术是阴影映射 shadow mapping。通常在视频游戏中使用,效果好且速度快。
阴影映射是一项两步运行的技术:
• 从光的角度看,3D 场景首先在纹理中渲染。 该纹理将包含所有片段的深度(光源和片段之间的距离),而不是包含片段的颜色。 此纹理称为阴影贴图 shadow map。
• 然后在渲染 3D 场景时使用阴影贴图。在屏幕上绘制片段时,我们可以从阴影图中知道光源和当前片段之间是否还有另一个片段。如果是这种情况,则该片段在阴影中,应该将其绘制得更暗一些。
可以对水体焦散运行类似的方法,首先在纹理中渲染水下环境,然后使用该纹理来计算光线与环境之间的交点。 除了渲染片段深度之外,还渲染了环境图中的片段位置,RGB 信道存储 XYZ 位置,alpha 信道存储深度:
计算光线与环境的交点
现在有了水下环境图,需要计算折射光线与环境之间的交点。
算法如下:
1. 从光线与水面的交点开始
2. 使用折射函数计算折射
3. 从当前位置沿折射射线方向移动,环境图纹理的一个像素
4. 将存储在当前环境纹理像素中的环境深度与当前深度进行比较。如果环境深度大于当前深度,则意味着我们需要走得更远,因此我们再次运行步骤 3。如果环境深度小于当前深度,则意味着光线在您从中读取的位置击中了环境。
焦散纹理
一旦找到交点,就可以使用 Evan Wallace 在其文章中提到的技术来计算焦散强度(和焦散强度纹理)。 产生的纹理如下所示:
该纹理包含 3D 空间每个点的光强度信息。当渲染最终场景时,我们可以从焦散纹理中读取此光强度,并得到以下结果:
其他
本文重点讨论焦散的计算,但此 demo 中还有其他技术。
关于水面渲染,使用了天空盒纹理和立方体贴图进行反射。还使用简单的屏幕空间折射(请参阅有关屏幕空间反射和折射的这篇文章)在水面上应用了折射,该技术在物理上不是正确的,但在视觉上吸引人且快速。 此外,添加了色差以提高真实感。
后续会介绍下前面焦散纹理的生成方法,以及简单解读下原代码以及 ThreeJS 的使用