Introduction
本文基本理论及其实现参考GPU Gems 2[1]和Scratchapixel[2]
大气的效果主要是由大小的粒子经过大量散射后带来的结果。 其中光被分子大小的粒子散射称之为Rayleigh scattering,而更大一些的沙尘散射被称之为Mie scattering。 大气散射的结果主要是由这两部分组成。 本文未实现多重散射,以及点光源等更通用的方法,而是使用单级散射配合上平行光的结果。
Out-Scattering And In-Scattering
Out-Scattering
In-Scattering
如若有一平行光(这里假定太阳无限远,则光源近似于平行光)入射大气相交C点,则由于Out-Scattering的影响光照强度将会有一定衰减,其中T为衰减系数。
IP=IC⋅T(CP)
从P点的光照抵达A点时,会发生由P点的大气粒子经过散射造成的In-Scattering,从CP方向变为PA方向,散射系数定义为S,λ为光的波长,θ为散射角度,h为相对海拔高度。 同样也会由于在路径PA上造成衰减。
IA=IP⋅S(λ,θ,h)⋅T(PA)
S表示的是在某一方向上的散射系数,以Rayleigh scattering为例
S(λ,θ,h)=2π2(n2−1)2Nρ(h)λ41(1+cos2θ)
Parameter |
Meaning |
λ |
入射光波长 |
θ |
散射角度 |
h |
当前点的海拔 |
N |
分子数量密度 |
ρ(h) |
关于海拔1位置的相对密度 |
其中S(λ,θ,h)可以拆分为三项,P(θ)就是体渲染中常见的Phase Function
S(λ,θ,h)=β(λ,h)P(θ)=β(λ)ρ(h)P(θ)
无论是Mie还是Rayleigh其大气密度都随着海拔接近于指数下降
ρ(h)=e−Hh
Rayleigh Scattering And Mie Scattering
这里β(λ,h)可以通过对S(λ,θ,h)在整个球面上进行积分,以算出在总方向上的散射系数。 其中可以预计算完β=(33.1e−6,13.5e−6,5.8e−6)m−1
β(λ,h)Rayleigh===∫02π∫0πS(λ,θ,h)sinθdθdϕ2π2(n2−1)2Nρ(h)λ41∫02π∫0π(1+cos2θ)sinθdθdϕ28π3(n2−1)2Nρ(h)λ41
大气中的吸收系数可以忽略不计,因此散射系数基本由Phase Function决定。 其对应的Phase Fucntion为
P(h)==β(λ)S(λ,θ,h)16π3(1+cos2θ)
Mie Scattering不像Rayleigh那样,其大小变化也接近于从海拔1位置处指数下降,这里取β(λ,0)=210e−5m−1,g=0.76
β(λ,h)Mie=β(λ,0)⋅e−HMh
P(h)=8π3(2+g2)(1+g2−2gcos2θ)23(1−g2)(1+cos2θ),g∈(−1,1)
Optical Depth
衰减指数T与海拔高度有关,非定值,因此需要在海拔上做积分
T(PA)=e−∫PAβ(λ,h)ds=e−β(λ)∫PAρ(h)ds
此处令光学深度D为
D(PA)=∫PAρ(h)ds
则最终进入到A处的光照为
IA===∫ABIP⋅S(λ,θ,h)⋅T(PA)d∫ABIC⋅T(CP)⋅S(λ,θ,h)⋅T(PA)dsIC⋅β(λ)⋅ρ(h)∫ABe−β(λ)(D(CP)+D(PA))⋅ρ(h)ds
Implementation
RayMarching
这里借鉴英伟达Demo,利用vs来生成一个全屏的三角形[10],以方便在PS中RayMarching(因为在VS中无需任何输入)。 此处由于使用了真实的物理数值,因此在Shader实现中需要注意float会溢出的问题,可以通过暂存为double或做单位转换来解决
具体实现可参考Sophia渲染器中shader/skyRayCasting/部分
struct output{
};
output main(uint id : SV_VertexID)
{
output o;
o.texCoord = float2((id << 1) & 2, id & 2);
o.position = float4(o.texCoord * float2(2, -2) + float2(-1, 1), 0, 1);
return o;
}
Mesh Based
这一部分其基本原理与上述是一致的,主要参考于GPU Gems 2[1]。 不同之处在于,Sean O’Neil提出了一个将大气预计算到LUT的方式,具体实现可以参考Sophia渲染器中shader/sky/部分
共计四张纹理,其中光学深度为HDR(部分会超过1)。 密度LUT中x表示海拔(映射为(0, 1))高度,y表示垂直角度(0时笔直朝上,1垂直向下)。 RGBA中Rayleigh与Mie各占一半,Rayleigh部分的密度以及Depth如下图
实现可以参考test中的earth的MakeOpticalDepthBuffer函数
Result
RayMarching结果较好,无论是在太空中或者是球内都有较为不错的结果
Mesh Based远观效果是可以的,这里的球生成的时候只有64*64的细分程度,再配合上LUT,帧率能跑到1000+fps。 但其缺点是不可近看,在低面数下插值涂抹感比较严重,除非将球的面数成倍的增加,这样就带来了带宽和计算压力显得不是很必要(这里借鉴GPU Gems没有使用地球大小的参数,因此样式略微不同)
因此最佳的方式应该是,RayMarching+LUT的实现方式(将在未来更新)。 原本做Mesh Based是希望能够将地面一次性也做displacement,不过由于效果一般,因此也没有做球内视角。
Reference
- GPU Gems 2: Accurate Atmospheric Scattering:基于Mesh的大气渲染模拟实现参考
- Simulating the Colors of the Sky:基本大气渲染理论推导参考
- Natural Earth III:地球纹理
- Practical Implementation of Light Scattering Effects Using Epipolar Sampling and 1D Min/Max Binary Trees
- Volumetric Atmospheric Scattering:大气理论推导及Shader实现参考
- Flexible Physical Accurate Atmosphere Scattering:预计算LUT对照参考
- 基于物理的大气渲染
- Display of the Earth Taking into Account Atmospheric Scattering
- Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time:93年 Nishita关于大气渲染的推导
- Vertex Shader Tricks:VS生成全屏Quad做单像素PS的解释