UE指数高度雾(Exponential Height Fog)
文件相关
// C++
HeightFogComponent.cpp
FogRendering.cpp
ExponentialHeightFogComponent.h
// Shaders
HeightFogCommon.ush
/*
* Others,
* different platforms or application scenarios, etc.
*/
MobileBasePassVertexShader.usf // Base pass vertex shader used with forward shading
MobileBasePassPixelShader.usf // Base pass pixel shader used with forward shading
HeightFogVertexShader.usf // Scene fogging vertex shader
HeightFogPixelShader.usf // Scene fogging pixel shader
...
其中最泛的函数
half4 CalculateHeightFog(float3 WorldPositionRelativeToCamera);
无论是VertexFog或是PixelFog, 基本都可直接调用该函数, 获取HeightFog和场景Color混合, 不同在于性能和效果权衡下
雾的计算
计算雾效浓度衰减的公式如下
// Calculate the line integral of the ray from the camera to the receiver position through the fog density function
// The exponential fog density function is d = GlobalDensity * exp(-HeightFalloff * z)
float CalculateLineIntegralShared(float FogHeightFalloff, float RayDirectionZ, float RayOriginTerms)
{
float Falloff = max(-127.0f, FogHeightFalloff * RayDirectionZ); // if it's lower than -127.0, then exp2() goes crazy in OpenGL's GLSL.
float LineIntegral = ( 1.0f - exp2(-Falloff) ) / Falloff;
float LineIntegralTaylor = log(2.0) - ( 0.5 * Pow2( log(2.0) ) ) * Falloff; // Taylor expansion around 0
return RayOriginTerms * ( abs(Falloff) > FLT_EPSILON2 ? LineIntegral : LineIntegralTaylor );
}
// Calculate the "shared" line integral (this term is also used for the directional light inscattering) by adding the two line integrals together (from two different height falloffs and densities)
float ExponentialHeightLineIntegralShared = CalculateLineIntegralShared(FogStruct.ExponentialFogParameters.y, RayDirectionZ, RayOriginTerms) + CalculateLineIntegralShared(FogStruct.ExponentialFogParameters2.y, RayDirectionZ, RayOriginTermsSecond);
float DirExponentialHeightLineIntegral = ExponentialHeightLineIntegralShared * max(RayLength - StartDistance, 0.0f);
float ExponentialHeightLineIntegral = ExponentialHeightLineIntegralShared * RayLength;
最终雾效颜色的计算公式如下
half3 FogColor = (InscatteringColor) * (1 - ExpFogFactor) + DirectionalInscattering;
其中因式项拆分
DirectionalInscattering, 光照散射部分, 同时便于调节视野雾效开始距离
// Calculate the line integral of the eye ray through the haze, using a special starting distance to limit the inscattering to the distance
float DirectionalInscatteringStartDistance = FogStruct.InscatteringLightDirection.w;
float DirExponentialHeightLineIntegral = ExponentialHeightLineIntegralShared * max(RayLength - DirectionalInscatteringStartDistance, 0.0f);
// Calculate the amount of light that made it through the fog using the transmission equation
half DirectionalInscatteringFogFactor = saturate(exp2(-DirExponentialHeightLineIntegral));
// Final inscattering from the light
DirectionalInscattering = DirectionalLightInscattering * (1 - DirectionalInscatteringFogFactor);
ExpFogFactor, 雾效因子, 控制与输出颜色的混合程度
其中,FogDensity
FogHeightFalloff
CameraWorldPosition.z - FogHeight
公式对应的代码部分
float RayOriginTerms = FogDensity * exp2(-FogHeightFalloff * (CameraWorldPosition.z - FogHeight));
InscatteringColor, 光照本身的散射颜色部分
其中,FogHeightFalloff
RayDirectionZ
加入散射, 雾效更具光感,
其中,lightColor
dot(cameraToReceiver.xyz, lightDir.xyz)
exponent
最后,
其中,g
为InscatteringColor
对应最终雾效颜色的计算公式如开始所述.
设置相关
- Apply Fogging, 默认是
true
, 该材质是否参数雾效计算 - Apply Cloud Fogging, 默认是
false
, 计算云的贡献时, 会考虑雾效的影响 - Compute Fog Per Pixel, 默认是
false
, 该材质是否在Frag
阶段计算雾效, 否则是在Vert
阶段计算雾效, 对应宏MATERIAL_COMPUTE_FOG_PER_PIXEL
, 应用范围为材质级别
- Disable vertex fogging on mobile shaders, 默认是
true
, 移动平台关闭雾效果, 对应宏PROJECT_MOBILE_DISABLE_VERTEX_FOG
- Vertex Fogging to Opaque, 默认是
false
, 不透明BasePass且需要雾效下是否使用顶点雾,false
时使用在Frag
阶段计算雾效, 对应宏PROJECT_VERTEX_FOGGING_FOR_OPAQUE
#define USE_VERTEX_FOG (!PROJECT_MOBILE_DISABLE_VERTEX_FOG) && MATERIAL_ENABLE_TRANSLUCENCY_FOGGING
#define NEEDS_BASEPASS_VERTEX_FOGGING (TRANSLUCENCY_NEEDS_BASEPASS_FOGGING && !MATERIAL_COMPUTE_FOG_PER_PIXEL || OPAQUE_NEEDS_BASEPASS_FOGGING && PROJECT_VERTEX_FOGGING_FOR_OPAQUE)
#define NEEDS_BASEPASS_PIXEL_FOGGING (TRANSLUCENCY_NEEDS_BASEPASS_FOGGING && MATERIAL_COMPUTE_FOG_PER_PIXEL || OPAQUE_NEEDS_BASEPASS_FOGGING && !PROJECT_VERTEX_FOGGING_FOR_OPAQUE)
使用
其他材质或Shader使用姿势
ush
部分
#if PACK_INTERPOLANTS
float4 PackedInterps[NUM_VF_PACKED_INTERPOLANTS];
UNROLL
for(int i = 0; i < NUM_VF_PACKED_INTERPOLANTS; ++i)
{
PackedInterps[i] = 0;
}
#endif
#if USE_VERTEX_FOG
float4 VertexFog = CalculateHeightFog(WorldPosition.xyz - ResolvedView.TranslatedWorldCameraOrigin);
// Ignore the sky atmosphere impact.
// #if PROJECT_SUPPORT_SKY_ATMOSPHERE && MATERIAL_IS_SKY==0 // Do not apply aerial perpsective on sky materials
// #endif
#if PACK_INTERPOLANTS
PackedInterps[0] = VertexFog;
#else
Output.BasePassInterpolants.VertexFog = VertexFog;
#endif // PACK_INTERPOLANTS
#endif
usf
部分
half4 VertexFog = half4(0, 0, 0, 1);
#if USE_VERTEX_FOG
#if PACK_INTERPOLANTS
VertexFog = PackedInterpolants[0];
#else
VertexFog = BasePassInterpolants.VertexFog;
#endif
#endif
// On mobile, water (an opaque material) is rendered as trnaslucent with forced premultiplied alpha blending (see MobileBasePass::SetTranslucentRenderState)
#if MATERIALBLENDING_ALPHACOMPOSITE || MATERIAL_SHADINGMODEL_SINGLELAYERWATER
OutColor = half4(Color * VertexFog.a + VertexFog.rgb * ShadingModelContext.Opacity, ShadingModelContext.Opacity);
#elif MATERIALBLENDING_ALPHAHOLDOUT
// not implemented for holdout
OutColor = half4(Color * VertexFog.a + VertexFog.rgb * ShadingModelContext.Opacity, ShadingModelContext.Opacity);
#elif MATERIALBLENDING_TRANSLUCENT
OutColor = half4(Color * VertexFog.a + VertexFog.rgb, ShadingModelContext.Opacity);
#elif MATERIALBLENDING_ADDITIVE
OutColor = half4(Color * (VertexFog.a * ShadingModelContext.Opacity.x), 0.0f);
#elif MATERIALBLENDING_MODULATE
half3 FoggedColor = lerp(half3(1, 1, 1), Color, VertexFog.aaa * VertexFog.aaa);
OutColor = half4(FoggedColor, ShadingModelContext.Opacity);
#else
OutColor.rgb = Color * VertexFog.a + VertexFog.rgb;