2023-06-29 11:55:02 +08:00

203 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## UE4 渲染功能探究
New: Planar Reflections
New: High Quality Reflections
## UE4.26 SingleLayerWater笔记
官方论坛讨论
https://forums.unrealengine.com/development-discussion/rendering/1746626-actually-realistic-water-shader#post1789028
### SingleLayerCommon.ush
计算光照强度、透明度。
struct WaterVolumeLightingOutput
{
float3 Luminance;
float3 WaterToSceneTransmittance;
float3 WaterToSceneToLightTransmittance;
};
Output.Luminance = WaterVisibility * (ScatteredLuminance + Transmittance * BehindWaterSceneLuminance);
Output.WaterToSceneTransmittance = Transmittance;
Output.WaterToSceneToLightTransmittance;
目前没有开启RayMarching,所以核心代码为:
```
const float3 OpticalDepth = ExtinctionCoeff * BehindWaterDeltaDepth;
float3 Transmittance = exp(-OpticalDepth);
float3 ScatteredLuminance = ScatteringCoeff * (AmbScattLuminance + SunScattLuminance * DirectionalLightShadow);
ScatteredLuminance = (ScatteredLuminance - ScatteredLuminance * Transmittance) / ExtinctionCoeffSafe;
// Apply Fresnel effect to out-scattering towards the view
ScatteredLuminance *= CameraIsUnderWater ? 1.0 : (1.0 - EnvBrdf); // Under water is less visible due to Fresnel effect
Transmittance *= CameraIsUnderWater ? (1.0 - EnvBrdf) : 1.0; // Above " " " " "
// Add single in-scattering apply colored transmittance to scene color
Output.Luminance = WaterVisibility * (ScatteredLuminance + Transmittance * (BehindWaterSceneLuminance* ColorScaleBehindWater));
Output.WaterToSceneTransmittance = Transmittance;
Output.WaterToSceneToLightTransmittance = Transmittance * MeanTransmittanceToLightSources;
```
```c++
const float BehindWaterDeltaDepth = CameraIsUnderWater ? WaterDepth : max(0.0f, SceneDepth - WaterDepth);
const float3 ScatteringCoeff = max(0.0f, GetSingleLayerWaterMaterialOutput0(MaterialParameters));
const float3 AbsorptionCoeff = max(0.0f, GetSingleLayerWaterMaterialOutput1(MaterialParameters));
const float PhaseG = clamp(GetSingleLayerWaterMaterialOutput2(MaterialParameters), -1.0f, 1.0f);
//Sample the optional Material Input ColorScaleBehindWater and fade it out at shorelines to avoid hard edge intersections
float3 ColorScaleBehindWater = lerp(1.0f, max(0.0f, GetSingleLayerWaterMaterialOutput3(MaterialParameters)), saturate(BehindWaterDeltaDepth * 0.02f));
const float3 ExtinctionCoeff = ScatteringCoeff + AbsorptionCoeff;
// Max to avoid division by 0 with the analytical integral below.
// 1e-5 is high enough to avoid denorms on mobile
const float3 ExtinctionCoeffSafe = max(ScatteringCoeff + AbsorptionCoeff, 1e-5);
float DirLightPhaseValue = 0.0f; // Default when Total Internal Reflection happens.
{
#if SIMPLE_SINGLE_LAYER_WATER
DirLightPhaseValue = IsotropicPhase();
#else
float IorFrom = 1.0f; // assumes we come from air
float IorTo = DielectricF0ToIor(DielectricSpecularToF0(Specular)); // Wrong if metal is set to >1. But we still keep refraction on the water surface nonetheless.
const float relativeIOR = IorFrom / IorTo;
float3 UnderWaterRayDir = 0.0f;
if (WaterRefract(MaterialParameters.CameraVector, MaterialParameters.WorldNormal, relativeIOR, UnderWaterRayDir))
{
DirLightPhaseValue = SchlickPhase(PhaseG, dot(-ResolvedView.DirectionalLightDirection.xyz, UnderWaterRayDir));
}
#endif
}
// We also apply transmittance from light to under water surface. However, the scene has been lit by many sources already.
// So the transmittance toabove surface is simply approximated using the travel distance from the scene pixel to the water top, assuming a flat water surface.
// We cannot combine this transmittance with the transmittance from view because this would change the behavior of the analytical integration of light scattering integration.
const float3 BehindWaterSceneWorldPos = SvPositionToWorld(float4(MaterialParameters.SvPosition.xy, SceneDeviceZ, 1.0));
const float DistanceFromScenePixelToWaterTop = max(0.0, MaterialParameters.AbsoluteWorldPosition.z - BehindWaterSceneWorldPos.z);
const float3 MeanTransmittanceToLightSources = exp(-DistanceFromScenePixelToWaterTop * ExtinctionCoeff);
#if SIMPLE_SINGLE_LAYER_WATER
const float3 BehindWaterSceneLuminance = 0.0f; // Cannot read back the scene color in this case
#else
// We use the pixel SvPosition instead of the scene one pre refraction/distortion to avoid those extra ALUs.
float3 BehindWaterSceneLuminance = SceneColorWithoutSingleLayerWaterTexture.SampleLevel(SceneColorWithoutSingleLayerWaterSampler, ViewportUV, 0).rgb;
BehindWaterSceneLuminance = MeanTransmittanceToLightSources * (USE_PREEXPOSURE ? ResolvedView.OneOverPreExposure : 1.0f) * BehindWaterSceneLuminance;
#endif
float3 SunScattLuminance = DirLightPhaseValue * SunIlluminance;
float3 AmbScattLuminance = IsotropicPhase() * AmbiantIlluminance;
#define VOLUMETRICSHADOW 0
#if !VOLUMETRICSHADOW || SIMPLE_SINGLE_LAYER_WATER
const float3 OpticalDepth = ExtinctionCoeff * BehindWaterDeltaDepth;
float3 Transmittance = exp(-OpticalDepth);
float3 ScatteredLuminance = ScatteringCoeff * (AmbScattLuminance + SunScattLuminance * DirectionalLightShadow);
ScatteredLuminance = (ScatteredLuminance - ScatteredLuminance * Transmittance) / ExtinctionCoeffSafe;
#else
// TODO Make the volumetric shadow part work again
float3 Transmittance = 1.0f;
float3 ScatteredLuminance = 0.0f;
const float RayMarchMaxDistance = min(BehindWaterDeltaDepth, 200.0f); // 20 meters
const float RayMarchStepSize = RayMarchMaxDistance / 10.0f; // Less samples wil lresult in a bit brighter look due to TransmittanceToLightThroughWater being 1 on a longer first sample. Would need it part of analiytical integration
const float ShadowDither = RayMarchStepSize * GBufferDither;
for (float s = 0.0f; s < RayMarchMaxDistance; s += RayMarchStepSize)
{
// Only jitter shadow map sampling to not lose energy on first sample
float Shadow = ComputeDirectionalLightDynamicShadowing(MaterialParameters.AbsoluteWorldPosition - (s + ShadowDither)*MaterialParameters.CameraVector, GBuffer.Depth);
float3 WP = MaterialParameters.AbsoluteWorldPosition - s * MaterialParameters.CameraVector;
float WaterHeightAboveSample = max(0.0, MaterialParameters.AbsoluteWorldPosition.z - WP.z);
float3 TransmittanceToLightThroughWater = 1.0; // no self shadow, same energy as above analytical solution
//float3 TransmittanceToLightThroughWater = exp(-ExtinctionCoeff * WaterHeightAboveSample); // self shadow as transmittance to water level, close to reference, depends a bit on sample count due to first sample being critical for dense medium
float3 SampleTransmittance = exp(-ExtinctionCoeff * RayMarchStepSize); // Constant
float3 SS = (ScatteringCoeff * TransmittanceToLightThroughWater * (SunScattLuminance * Shadow + AmbScattLuminance));
ScatteredLuminance += Transmittance * (SS - SS * SampleTransmittance) / ExtinctionCoeffSafe;
Transmittance *= SampleTransmittance;
}
// The rest of the medium
const float3 OpticalDepth2 = ExtinctionCoeff * max(0.0, BehindWaterDeltaDepth - RayMarchMaxDistance);
if (any(OpticalDepth2 > 0.0f))
{
float3 Transmittance2 = exp(-OpticalDepth2);
float3 ScatteredLuminance2 = ScatteringCoeff * (SunScattLuminance + AmbScattLuminance);
ScatteredLuminance += Transmittance * (ScatteredLuminance2 - ScatteredLuminance2 * Transmittance2) / ExtinctionCoeffSafe;
Transmittance *= Transmittance2;
}
#endif
// Apply Fresnel effect to out-scattering towards the view
ScatteredLuminance *= CameraIsUnderWater ? 1.0 : (1.0 - EnvBrdf); // Under water is less visible due to Fresnel effect
Transmittance *= CameraIsUnderWater ? (1.0 - EnvBrdf) : 1.0; // Above " " " " "
// Add single in-scattering apply colored transmittance to scene color
Output.Luminance = WaterVisibility * (ScatteredLuminance + Transmittance * (BehindWaterSceneLuminance* ColorScaleBehindWater));
Output.WaterToSceneTransmittance = Transmittance;
Output.WaterToSceneToLightTransmittance = Transmittance * MeanTransmittanceToLightSources;
}
```
海洋是不透明的使用SceneColor缓存合成出的透明效果。
## GDC2012 神秘海域3演讲
### 渲染方案
### FlowShader
没看懂为什么需要用2张贴图叠加是因为要过度么
4.5.1.1 Flow Map变体《神秘海域3》Flow Map + Displacement
另外Flow Map可以和其他渲染技术结合使用比如《神秘海域3》中的Flow Map + Displacement
![image](https://pic4.zhimg.com/80/v2-a973e1d3ce0356a1f0823c644e14a21b_720w.jpg)
4.5.1.2 Flow Map变体《堡垒之夜》Flow Map + Distance Fields + Normal Maps
以及《堡垒之夜》中的Flow Map + Distance Fields + Normal Maps [GDC 2019, Technical Artist Bootcamp Distance Fields and Shader Simulation Tricks]
4.5.1.3 Flow Map变体《神秘海域4》Flow Map + Wave Particles
或者《神秘海域4》中的Flow Map + Wave Particles[SIGGRAPH 2016, Rendering Rapids in Uncharted 4],都是进阶模拟水体表面流动与起伏效果的不错选择。
### Wave System
如果我们能找到一个好的模型,程序化的几何和动画是不错的。
仿真计算成本太高即使在SPU中设计者也很难控制。
Perlin噪音效果在视觉上不是很好往往看起来很人工化
FFT技术很好但是参数很难被艺术家控制和调整。也是很难搞好的
**Gerstner waves**
简单易于控制效果,但高频细节不够多,只能叠加几组大浪,否则太消耗资源。
**FFT Waves**
真实,细节丰富。但是美术难以控制效果。
神秘海域3采用4组Gerstner waves+4组波动粒子的方式来实现Wave Vector Displacement。
#### 大浪
大浪采用贝塞尔曲线建模完成
**之后再叠加大浪**
![image](D:/youdaonote-pull-master/youdaonote/youdaonote-images/93488B8FEA8F4AA689BD7E1691988284.octet-stream)
这是整个波系的局部公式。
bspline是一个均匀的、非理性的bspline。我们本可以使用贝塞尔但它需要更多的代码。
grid(u,v)函数返回一个给定坐标u,v的标量值。在这种情况下我们有一个波标的乘数
## Sea of Thieves approach [Ang, 2018]
## Crest Siggraph2019
### Light Scattering
使用了类似盗贼之海的光线散射算法,光线散射项是基于海面置换项的水平长度。这里补充一下:使它在我们的框架中更好地工作--我们通过将置换项除以波长来做一种特殊的归一化,并将这个归一化版本用于光散射项。
基于海平面高度的散射在海洋参数发生改变时容易出问题。
如果将置换项除以波长,就可以针对大波与小波进行缩放。
### Shadering
Cascade scale used to scale shading inputs
Normals
Foam
Underwater bubbles
Works for range of viewpoints
Breaks up patterns
Combats mipmapping
Increase visible range of detail
Doubles texture samples