# Volume Rendering

Untitled

在光传播时,抽象点来说就是分子把收集到的光带到你的眼睛里,但这过程中会发生各种情况扰乱光的传播。

  • 通过介质传播到眼睛的光束会由于以下原因损失能量
    • 吸收(Absorption)吸收了一部份的光,把收集到的光吸收了一部份
    • 外散射(Out-scattering)把一部份光散射出去给别的分子,从而光能力不能到你的眼睛
  • 通过介质传播到眼睛的光束会由于以下原因损失能量
    • 自发光(Emissive)就是自己的光能量
    • 内散射(In- Scattering)把从四面八方发射出来的光收集起来带到我们的眼睛,可以理解为 Irradiance

Untitled

渲染时候用 Raymrach 做法,摄像机每步想前前进然后再想太阳方向累加求积分,在 ue volume cloud 中主要做了 2 次,算 LuminanceIntegral 时候

# Scattering

""
1
2
3
4
float3 LuminanceIntegral = (ScatteredLuminance - ScatteredLuminance * SafePathSegmentTransmittance) / SafeExtinctionCoefficients;
//等价,在ue水体渲染就用了这公式
float3 ScatteringAmount = saturate(ScatteringCoeff * (1.0f - Transmittance) / ExtinctionCoeffSafe);

算 Transmittance,其中 extension = Absorption + Scattering , S 就是距离或者深度,这个 extension 可以自定义,也就是说这三个数都可以自定义

L(s)=e(σa+σs)sL(s)=eσts\begin{array}{l}L(s) = e^{-(\sigma_a + \sigma_s) s}\\L(s) = e^{-\sigma_t s}\end{array}

意思就是说越深的地方透过越少

Untitled

散射类型也分很多种

  • 米氏散射(Mie scattering): 散射后看不出具体颜色,仅仅是黑白灰(反映光强大小)。是大气中粒子的直径与辐射的波长相当时发生的散射。这种散射主要由大气中的微粒,如烟、尘埃、小水滴及气溶胶等引起。米氏散射的散射强度与频率的二次方成正比,并且散射在光线向前方向比向后方向更强,方向性比较明显
  • ** 瑞利散射(Rayleigh scattering):** 散射波长与入射波长相同;散射光强与波长的四次方成反比;散射光强按空间方向成哑铃形角分布。渲染天穹会用的到,另外关于偏振态变化:自然光入射时,各方向的散射光一般为部分偏振光,垂直入射光方向的散射光是线偏振光,沿入射光或其逆方向的散射光是自然光。

注:大于一定尺寸的是米氏散射,小于一定尺寸的是瑞利散射

# Phase Function

光从进入 volume 到出来的时候,只有一小部分入射光向眼睛散射。多少取决于光线和视线方向之间的角度。而这个函数就是概率,算出有多少光线进入我们的眼睛,也就是说每次相机打 raymrach 做积分时候,这就是眼睛与 direction light 的 dot。因为定向光和相机摄像在积分时候是不会变的,所以只用算一次就行了,其中写成

fp(x,θ)f_p(x, \theta)

x 就是 phase,自定义的参数,在 Nubis 和 ue 都是两双 Phase Function 算出最终 Phase

其中在发生散射时候介质(Participating Media)有两种属性:

  • 各项同性(Isotropic)

    就是各个方向发生的散射几率都是相同的,也就是生活中的漫反射,其公式很简单

    ""
    1
    2
    3
    4
    float IsotropicPhase()
    {
    return 1.0f / (4.0f * PI);
    }

  • 各项异性(Anisotropic)

    光线只朝着一个方向大量散射,例如云光照、头发

    Untitled

    最常用之一 Phase 函数就是 HG 函数

    fp(x,θ)=14π1g2(1+g22gcosθ)32.f_p(x,\theta) = \frac{1}{4 \pi} \frac{1 - g^2}{(1 + g^2 - 2 g \cos \theta)^{\frac{3}{2}}}.

    ""
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    float HenyeyGreensteinPhase(float G, float CosTheta)
    {
    float Numer = 1.0f - G * G;
    float Denom = 1.0f + G * G + 2.0f * G * CosTheta;
    return Numer / (4.0f * PI * Denom * sqrt(Denom));
    }
    //HG函数的近似SchlickPhase
    float SchlickPhaseFromK(float K, float CosTheta)
    {
    const float SchlickPhaseFactor = 1.0f + K * CosTheta;
    const float PhaseValue = (1.0f - K * K) / (4.0f * PI * SchlickPhaseFactor * SchlickPhaseFactor);
    return PhaseValue;
    }
    float SchlickPhase(float G, float CosTheta)
    {
    const float K = 1.55f * G - 0.55f * G * G * G;
    return SchlickPhaseFromK(K, CosTheta);
    }

    这是个对 MieScattering 的一个模拟

# 代码

最后求出

""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
float HenyeyGreensteinPhase(float G, float CosTheta)
{
float Numer = 1.0f - G * G;
float Denom = 1.0f + G * G + 2.0f * G * CosTheta;
return Numer / (4.0f * PI * Denom * sqrt(Denom));
}

float SamplePhaseFunction(in float PhaseCosTheta, in float PhaseG, in float PhaseG2, in float PhaseBlend)
{
PhaseG = clamp(PhaseG, -0.999f, 0.999f);
PhaseG2 = clamp(PhaseG2, -0.999f, 0.999f);
PhaseBlend = clamp(PhaseBlend, 0.0f, 1.0f);
float MiePhaseValueLight0 = HenyeyGreensteinPhase(PhaseG, -PhaseCosTheta);
float MiePhaseValueLight1 = HenyeyGreensteinPhase(PhaseG2, -PhaseCosTheta);
const float Phase = MiePhaseValueLight0 + PhaseBlend * (MiePhaseValueLight1 - MiePhaseValueLight0);
return Phase;
}

const float3 Light0Direction = ResolvedView.AtmosphereLightDirection[0].xyz;
const float3 Light1Illuminance = float3(0.0f, 0.0f, 0.0f);
const float3 Light1IlluminanceFinal = float3(0.0f, 0.0f, 0.0f);
const float3 Light1Direction = float3(0.0f, 0.0f, 0.0f);
const float3 wi0 = Light0Direction;
const float3 wi1 = Light1Direction;
const float3 wo = Raydir;
const float Phase0CosTheta = dot(wi0, wo);
const float Phase0 = SamplePhaseFunction(Phase0CosTheta, PhaseG, PhaseG2, PhaseBlend);
const float3 SafePathSegmentTransmittance = exp(-SafeExtinctionCoefficients * dtMeters);
float3 ScatteredLuminance = TransmittanceToLight0 * LightColor * Phase0;
float3 LuminanceIntegral = (ScatteredLuminance *(1 - SafePathSegmentTransmittance)) / SafeExtinctionCoefficients;
Luminance += LuminanceIntegral ;

更新于

请我喝[茶]~( ̄▽ ̄)~*

Natsuneko 微信支付

微信支付

Natsuneko 支付宝

支付宝

Natsuneko 贝宝

贝宝