Skip to content

Commit b0f7fc7

Browse files
implement volumetric fog.
1 parent 9d4da27 commit b0f7fc7

20 files changed

+405
-7
lines changed

PKRenderer/PKRenderer.vcxproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ Call "$(SolutionDir)Build\PKAssetTools.exe" "'$(ProjectDir)res\'" "'$(TargetDir)
546546
<ClInclude Include="src\Core\Services\AssetDatabase.h" />
547547
<ClInclude Include="src\ECS\Contextual\Engines\EngineDebug.h" />
548548
<ClInclude Include="src\ECS\Contextual\Engines\EnginePKAssetBuilder.h" />
549+
<ClInclude Include="src\Rendering\Passes\PassVolumeFog.h" />
549550
<ClInclude Include="src\Rendering\Passes\PassGeometry.h" />
550551
<ClInclude Include="src\Rendering\Passes\PassLights.h" />
551552
<ClInclude Include="src\Rendering\Passes\PassSceneGI.h" />
@@ -807,18 +808,23 @@ Call "$(SolutionDir)Build\PKAssetTools.exe" "'$(ProjectDir)res\'" "'$(TargetDir)
807808
<None Include="res\models\MDL_Columns.mdl" />
808809
<None Include="res\models\MDL_Spiral.mdl" />
809810
<None Include="res\shaders\CS_SceneGI_Bake.shader" />
811+
<None Include="res\shaders\CS_VolumeFogDepthMax.shader" />
812+
<None Include="res\shaders\CS_VolumeFogLightDensity.shader" />
813+
<None Include="res\shaders\CS_VolumeFogScatter.shader" />
810814
<None Include="res\shaders\includes\Blit.glsl" />
811815
<None Include="res\shaders\includes\ClusterIndexing.glsl" />
812816
<None Include="res\shaders\includes\Common.glsl" />
813817
<None Include="res\shaders\includes\Constants.glsl" />
814818
<None Include="res\shaders\includes\Encoding.glsl" />
815819
<None Include="res\shaders\includes\BRDF.glsl" />
816820
<None Include="res\shaders\includes\Reconstruction.glsl" />
821+
<None Include="res\shaders\includes\SharedVolumeFog.glsl" />
817822
<None Include="res\shaders\includes\SharedLights.glsl" />
818823
<None Include="res\shaders\includes\SharedSceneGI.glsl" />
819824
<None Include="res\shaders\includes\SharedShadowmapping.glsl" />
820825
<None Include="res\shaders\includes\SharedSurfaceShading.glsl" />
821826
<None Include="res\shaders\CS_SceneGI_Mipmap.shader" />
827+
<None Include="res\shaders\SH_VS_VolumeFogComposite.shader" />
822828
<None Include="res\shaders\SH_WS_PBR_Cloth.shader" />
823829
<None Include="res\shaders\includes\Utilities.glsl" />
824830
<None Include="res\shaders\includes\Lighting.glsl" />
@@ -908,6 +914,7 @@ Call "$(SolutionDir)Build\PKAssetTools.exe" "'$(ProjectDir)res\'" "'$(TargetDir)
908914
<ClCompile Include="src\Core\Services\Input.cpp" />
909915
<ClCompile Include="src\ECS\Contextual\Engines\EngineDebug.cpp" />
910916
<ClCompile Include="src\ECS\Contextual\Engines\EnginePKAssetBuilder.cpp" />
917+
<ClCompile Include="src\Rendering\Passes\PassVolumeFog.cpp" />
911918
<ClCompile Include="src\Rendering\Passes\PassGeometry.cpp" />
912919
<ClCompile Include="src\Rendering\Passes\PassLights.cpp" />
913920
<ClCompile Include="src\Rendering\Passes\PassSceneGI.cpp" />

PKRenderer/PKRenderer.vcxproj.filters

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,9 @@
14011401
<ClInclude Include="src\Rendering\Passes\PassSceneGI.h">
14021402
<Filter>Header Files</Filter>
14031403
</ClInclude>
1404+
<ClInclude Include="src\Rendering\Passes\PassVolumeFog.h">
1405+
<Filter>Header Files</Filter>
1406+
</ClInclude>
14041407
</ItemGroup>
14051408
<ItemGroup>
14061409
<None Include="include\glm\detail\func_common.inl">
@@ -1912,6 +1915,11 @@
19121915
<None Include="res\shaders\CS_SceneGI_Mipmap.shader" />
19131916
<None Include="res\shaders\includes\SharedSceneGI.glsl" />
19141917
<None Include="res\shaders\CS_SceneGI_Bake.shader" />
1918+
<None Include="res\shaders\includes\SharedVolumeFog.glsl" />
1919+
<None Include="res\shaders\CS_VolumeFogDepthMax.shader" />
1920+
<None Include="res\shaders\CS_VolumeFogLightDensity.shader" />
1921+
<None Include="res\shaders\CS_VolumeFogScatter.shader" />
1922+
<None Include="res\shaders\SH_VS_VolumeFogComposite.shader" />
19151923
</ItemGroup>
19161924
<ItemGroup>
19171925
<ClCompile Include="include\glm\detail\glm.cpp">
@@ -2130,6 +2138,9 @@
21302138
<ClCompile Include="src\Rendering\Passes\PassSceneGI.cpp">
21312139
<Filter>Source Files</Filter>
21322140
</ClCompile>
2141+
<ClCompile Include="src\Rendering\Passes\PassVolumeFog.cpp">
2142+
<Filter>Source Files</Filter>
2143+
</ClCompile>
21332144
</ItemGroup>
21342145
<ItemGroup>
21352146
<Library Include="libs\glfw3.lib" />

PKRenderer/res/shaders/CS_Bloom.shader

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ void main()
2020
int2 coord = int2(gl_GlobalInvocationID.xy);
2121
int2 size = imageSize(_DestinationTex).xy;
2222

23-
if (Greater(coord, size))
23+
if (GEqual(coord, size))
2424
{
2525
return;
2626
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#version 460
2+
#pragma PROGRAM_COMPUTE
3+
#include includes/SharedVolumeFog.glsl
4+
5+
const int2 offsets[4] = { int2(0,0), int2(0,1), int2(1,1), int2(1,0) };
6+
7+
layout(local_size_x = VOLUME_DEPTH_BATCH_SIZE_PX, local_size_y = VOLUME_DEPTH_BATCH_SIZE_PX, local_size_z = 1) in;
8+
void main()
9+
{
10+
int2 coord = int2(gl_GlobalInvocationID.xy) * 2;
11+
coord.x = min(coord.x, int(pk_ScreenParams.x - 2));
12+
coord.y = min(coord.y, int(pk_ScreenParams.y - 2));
13+
float2 uv = coord * pk_ScreenParams.zw;
14+
15+
float4 depths = LinearizeDepth(textureGatherOffsets(pk_ScreenDepth, uv, offsets));
16+
17+
float depth = depths.x;
18+
depth = max(depth, depths.y);
19+
depth = max(depth, depths.z);
20+
depth = max(depth, depths.w);
21+
22+
// Gathering can exceed the tile bounds by 1px. A negligible margin of error.
23+
atomicMax(PK_BUFFER_DATA(pk_VolumeMaxDepths, GetVolumeDepthTileIndex(uv)), floatBitsToUint(depth));
24+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#version 460
2+
#pragma PROGRAM_COMPUTE
3+
#include includes/Lighting.glsl
4+
#include includes/SharedVolumeFog.glsl
5+
#include includes/SharedSceneGI.glsl
6+
7+
float Density(float3 pos)
8+
{
9+
float fog = pk_Volume_ConstantFog;
10+
11+
fog += clamp(exp(pk_Volume_HeightFogExponent * (-pos.y + pk_Volume_HeightFogOffset)) * pk_Volume_HeightFogAmount, 0.0, 1e+3f);
12+
13+
float3 warp = pos;
14+
15+
fog *= NoiseScroll(warp, pk_Time.y * pk_Volume_WindSpeed, pk_Volume_NoiseFogScale, pk_Volume_WindDir.xyz, pk_Volume_NoiseFogAmount, -0.3, 8.0);
16+
17+
return max(fog * pk_Volume_Density, 0.0f);
18+
}
19+
20+
float3 GetAmbientColor(float3 position, float3 direction, float3 viewdir)
21+
{
22+
float anistropy = GetLightAnisotropy(viewdir, direction, pk_Volume_Anisotropy);
23+
24+
float4 scenegi = SampleGIVolumetric(position);
25+
float3 staticgi = SampleEnvironment(OctaUV(direction), 1.0f) * anistropy;
26+
return staticgi * scenegi.a + scenegi.rgb;
27+
}
28+
29+
layout(local_size_x = 16, local_size_y = 2, local_size_z = 16) in;
30+
void main()
31+
{
32+
uint3 id = gl_GlobalInvocationID;
33+
float2 uv = (VOLUME_SIZE_ST.zz + id.xy) * VOLUME_SIZE_ST.xy;
34+
35+
float zmax = VOLUME_LOAD_MAX_DEPTH(GetVolumeDepthTileIndex(uv));
36+
37+
// Triangle dither range is -1.5 - 1.5 and due to trilinear interpolation we also need to have 2 texels worth of coverage.
38+
float zmin = GetVolumeCellDepth(id.z - 3.0f);
39+
40+
float3 bluenoise = GetVolumeCellNoise(id);
41+
42+
float depth = GetVolumeCellDepth(id.z + NoiseUniformToTriangle(bluenoise.x));
43+
44+
// Texel is inside dither & trilinear interpolation range. Let's clamp it so that we can avoid light leaking through thin surfaces.
45+
if (zmin < zmax)
46+
{
47+
depth = min(zmax, depth);
48+
}
49+
50+
depth = max(pk_ProjectionParams.x, depth);
51+
52+
float3 worldpos = mul(pk_MATRIX_I_V, float4(ClipToViewPos(uv, depth), 1.0f)).xyz;
53+
54+
float3 viewdir = normalize(worldpos - pk_WorldSpaceCameraPos.xyz);
55+
56+
float3 color = GetAmbientColor(worldpos, normalize(bluenoise - 0.5f + float3(0, 1, 0)), viewdir);
57+
58+
LightTile tile = GetLightTile(GetTileIndexUV(uv, depth));
59+
60+
for (uint i = tile.start; i < tile.end; ++i)
61+
{
62+
color += GetVolumeLightColor(i, worldpos, viewdir, tile.cascade, pk_Volume_Anisotropy);
63+
}
64+
65+
float density = Density(worldpos);
66+
67+
float4 preval = tex2D(pk_Volume_InjectRead, ReprojectWorldToCoord(worldpos));
68+
float4 curval = float4(pk_Volume_Intensity * density * color, density);
69+
70+
curval = lerp(preval, curval, VOLUME_ACCUMULATION);
71+
curval.a = VOLUME_MIN_DENSITY + curval.a;
72+
73+
imageStore(pk_Volume_Inject, int3(id), curval);
74+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#version 460
2+
#pragma PROGRAM_COMPUTE
3+
#include includes/SharedVolumeFog.glsl
4+
5+
layout(local_size_x = 32, local_size_y = 2, local_size_z = 1) in;
6+
void main()
7+
{
8+
float4 accumulation = float4(0, 0, 0, 1);
9+
int3 pos = int3(gl_GlobalInvocationID.xy, 0);
10+
float3 vpos = ClipToViewPos((VOLUME_SIZE_ST.zz + pos.xy) * VOLUME_SIZE_ST.xy, 1.0f);
11+
12+
#pragma unroll VOLUME_DEPTH
13+
for (;pos.z < VOLUME_DEPTH; ++pos.z)
14+
{
15+
float2 depths = GetVolumeCellDepth(float2(pos.z, pos.z + 1.0f));
16+
float depth = lerp(depths.x, depths.y, 0.5f);
17+
float slicewidth = depths.y - depths.x;
18+
19+
float4 slice = imageLoad(pk_Volume_Inject, pos);
20+
21+
float transmittance = exp(-slice.a * slicewidth);
22+
float3 lightintegral = slice.rgb * (1.0f - transmittance) / slice.a;
23+
24+
accumulation.rgb += lightintegral * accumulation.a;
25+
accumulation.a *= transmittance;
26+
27+
float4 preval = tex2D(pk_Volume_ScatterRead, ReprojectViewToCoord(vpos * depth));
28+
float4 outval = lerp(preval, accumulation, VOLUME_ACCUMULATION);
29+
30+
imageStore(pk_Volume_Scatter, pos, outval);
31+
}
32+
}

PKRenderer/res/shaders/SH_VS_IBLBackground.shader

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#version 450
2+
#ZTest LEqual
3+
#ZWrite False
24
#include includes/Lighting.glsl
35
#include includes/Blit.glsl
46

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#version 460
2+
#BlendColor Add One SrcAlpha
3+
#include includes/SharedVolumeFog.glsl
4+
#pragma PROGRAM_VERTEX
5+
#include includes/Blit.glsl
6+
out float4 vs_TEXCOORD;
7+
8+
void main()
9+
{
10+
vs_TEXCOORD = PK_BLIT_VERTEX_TEXCOORD.xyxy;
11+
vs_TEXCOORD = float4(vs_TEXCOORD.xy, vs_TEXCOORD.xy * pk_ScreenParams.xy + pk_Time.ww * 1000);
12+
gl_Position = PK_BLIT_VERTEX_POSITION;
13+
};
14+
15+
#pragma PROGRAM_FRAGMENT
16+
float SampleDepth(float2 uv)
17+
{
18+
return LinearizeDepth(tex2D(pk_ScreenDepth, uv).r);
19+
}
20+
21+
in float4 vs_TEXCOORD;
22+
out float4 SV_Target0;
23+
void main()
24+
{
25+
float d = LinearizeDepth(tex2D(pk_ScreenDepth, vs_TEXCOORD.xy).r);
26+
float w = GetVolumeWCoord(d);
27+
28+
float3 offset = GlobalNoiseBlue(int2(vs_TEXCOORD.zw));
29+
offset -= 0.5f;
30+
offset *= VOLUME_COMPOSITE_DITHER_AMOUNT;
31+
32+
SV_Target0 = tex2D(pk_Volume_ScatterRead, float3(vs_TEXCOORD.xy, w) + offset.xyz);
33+
};

PKRenderer/res/shaders/includes/Common.glsl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ PK_DECLARE_CBUFFER(pk_PerFrameConstants, PK_SET_GLOBAL)
4343
float4x4 pk_MATRIX_I_VP;
4444
// Last view * projection matrix.
4545
float4x4 pk_MATRIX_L_VP;
46+
// Last view * projection * current inverse view matrix;
47+
float4x4 pk_MATRIX_LD_P;
4648
// Scene reflections exposure
4749
float pk_SceneOEM_Exposure;
4850
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#pragma once
2+
#include Common.glsl
3+
#include Noise.glsl
4+
5+
#define VOLUME_DEPTH 128
6+
#define VOLUME_INV_DEPTH 0.0078125f // 1.0f / 128.0f
7+
#define VOLUME_WIDTH 160
8+
#define VOLUME_HEIGHT 90
9+
#define VOLUME_SIZE_ST float3(0.00625f, 0.0111111111111111f, 0.5f) // (1.0f / 160.0f, 1.0f / 90.0f, 0.5f)
10+
#define VOLUME_COMPOSITE_DITHER_AMOUNT 2.0f * float3(0.00625f, 0.0111111111111111f, 0.0078125f)
11+
#define VOLUME_DEPTH_BATCH_SIZE_PX 16
12+
#define VOLUME_MIN_DENSITY 0.000001f
13+
#define VOLUME_ACCUMULATION clamp(20.0f * pk_DeltaTime.x, 0.01f, 1.0f)
14+
15+
PK_DECLARE_CBUFFER(pk_VolumeResources, PK_SET_SHADER)
16+
{
17+
float4 pk_Volume_WindDir;
18+
float pk_Volume_ConstantFog;
19+
float pk_Volume_HeightFogExponent;
20+
float pk_Volume_HeightFogOffset;
21+
float pk_Volume_HeightFogAmount;
22+
float pk_Volume_Density;
23+
float pk_Volume_Intensity;
24+
float pk_Volume_Anisotropy;
25+
float pk_Volume_NoiseFogAmount;
26+
float pk_Volume_NoiseFogScale;
27+
float pk_Volume_WindSpeed;
28+
};
29+
30+
PK_DECLARE_SET_SHADER uniform sampler3D pk_Volume_ScatterRead;
31+
PK_DECLARE_SET_SHADER uniform sampler3D pk_Volume_InjectRead;
32+
layout(rgba16f, set = PK_SET_SHADER) uniform image3D pk_Volume_Inject;
33+
layout(rgba16f, set = PK_SET_SHADER) uniform image3D pk_Volume_Scatter;
34+
35+
PK_DECLARE_BUFFER(uint, pk_VolumeMaxDepths, PK_SET_SHADER);
36+
37+
#define VOLUME_LOAD_MAX_DEPTH(index) uintBitsToFloat(PK_BUFFER_DATA(pk_VolumeMaxDepths, index))
38+
39+
float GetVolumeCellDepth(float index)
40+
{
41+
return pk_ProjectionParams.x * pow(pk_ExpProjectionParams.z, index / VOLUME_DEPTH);
42+
}
43+
44+
float2 GetVolumeCellDepth(float2 index)
45+
{
46+
return pk_ProjectionParams.xx * pow(pk_ExpProjectionParams.zz, index * VOLUME_INV_DEPTH);
47+
}
48+
49+
float GetVolumeWCoord(float depth)
50+
{
51+
return max(log2(depth) * pk_ExpProjectionParams.x + pk_ExpProjectionParams.y, 0.0);
52+
}
53+
54+
float3 GetVolumeCellNoise(uint3 id)
55+
{
56+
return GlobalNoiseBlue(id.xy + id.z * int2(VOLUME_WIDTH, VOLUME_HEIGHT) + int(pk_Time.w * 1000).xx);
57+
}
58+
59+
float3 ReprojectWorldToCoord(float3 worldpos)
60+
{
61+
float3 uvw = ClipToUVW(mul(pk_MATRIX_L_VP, float4(worldpos, 1.0f)));
62+
uvw.z = GetVolumeWCoord(LinearizeDepth(uvw.z));
63+
return uvw;
64+
}
65+
66+
float3 ReprojectViewToCoord(float3 viewpos)
67+
{
68+
float3 uvw = ClipToUVW(mul(pk_MATRIX_LD_P, float4(viewpos, 1.0f)));
69+
uvw.z = GetVolumeWCoord(LinearizeDepth(uvw.z));
70+
return uvw;
71+
}
72+
73+
uint GetVolumeDepthTileIndex(float2 uv)
74+
{
75+
return uint(uv.x * VOLUME_WIDTH) + VOLUME_WIDTH * uint(uv.y * VOLUME_HEIGHT);
76+
}

0 commit comments

Comments
 (0)