Skip to content

Commit f55aea1

Browse files
authored
Added atmospheric scattering (#518)
* Implemented atmospheric scattering, with dynamic sun position calculated from directional light * Changed default scene to use atmosphere instead of skysphere * Updated asset browser to create atmosphere shaders and materials * Updated actor creation menu to create atmosphere actors * Updated material system to change draw order within a pass (default draw order: 1000) * Updated skysphere material to be drawn early (draw order 100) * Set atmosphere draw order to 10 * Updated auto-exposure to skip first eye-adaptation frame
1 parent 3e305f4 commit f55aea1

File tree

18 files changed

+427
-13
lines changed

18 files changed

+427
-13
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<root>
2+
<shader>:Shaders\Atmosphere.ovfx</shader>
3+
<settings>
4+
<blendable>false</blendable>
5+
<backface_culling>false</backface_culling>
6+
<frontface_culling>true</frontface_culling>
7+
<depth_test>false</depth_test>
8+
<depth_writing>false</depth_writing>
9+
<color_writing>true</color_writing>
10+
<cast_shadows>false</cast_shadows>
11+
<receive_shadows>false</receive_shadows>
12+
<user_interface>false</user_interface>
13+
<gpu_instances>1</gpu_instances>
14+
<draw_order>10</draw_order>
15+
</settings>
16+
<uniforms>
17+
<uniform>
18+
<name>u_MiePreferredScatteringDirection</name>
19+
<value>0.75800002</value>
20+
</uniform>
21+
<uniform>
22+
<name>u_SunIntensity</name>
23+
<value>22</value>
24+
</uniform>
25+
<uniform>
26+
<name>u_SunPosition</name>
27+
<value>
28+
<x>0</x>
29+
<y>0.1</y>
30+
<z>-1</z>
31+
</value>
32+
</uniform>
33+
</uniforms>
34+
<features>DYNAMIC_SUN_POSITION</features>
35+
</root>

Resources/Engine/Materials/Skysphere.ovmat

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
<blendable>false</blendable>
55
<backface_culling>false</backface_culling>
66
<frontface_culling>true</frontface_culling>
7-
<depth_test>true</depth_test>
7+
<depth_test>false</depth_test>
88
<depth_writing>false</depth_writing>
99
<color_writing>true</color_writing>
1010
<cast_shadows>false</cast_shadows>
1111
<receive_shadows>false</receive_shadows>
1212
<user_interface>false</user_interface>
1313
<gpu_instances>1</gpu_instances>
14+
<draw_order>100</draw_order>
1415
</settings>
1516
<uniforms>
1617
<uniform>
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#feature OUTLINE_PASS
2+
#feature PICKING_PASS
3+
#feature DYNAMIC_SUN_POSITION
4+
5+
#shader vertex
6+
#version 450 core
7+
8+
#include ":Shaders/Common/Buffers/EngineUBO.ovfxh"
9+
#include ":Shaders/Common/Utils.ovfxh"
10+
#include ":Shaders/Common/Buffers/LightsSSBO.ovfxh"
11+
12+
layout (location = 0) in vec3 geo_Pos;
13+
layout (location = 1) in vec2 geo_TexCoords;
14+
15+
out VS_OUT
16+
{
17+
vec3 FragPos;
18+
flat vec3 SunPosition;
19+
} vs_out;
20+
21+
uniform vec3 u_SunPosition = vec3(0.0, 0.1, -1.0); // Default sun position
22+
23+
// Calculate the sun position based on the first directional light in the scene
24+
vec3 calculateSunPositionFromDirectionalLight()
25+
{
26+
for (int i = 0; i < ssbo_Lights.length(); ++i)
27+
{
28+
const mat4 light = ssbo_Lights[i];
29+
const int lightType = int(light[3][0]);
30+
31+
if (lightType == 1)
32+
{
33+
vec3 lightDirection = -light[1].xyz;
34+
return normalize(lightDirection);
35+
}
36+
}
37+
38+
// If no directional light is found, use the sun position uniform
39+
return u_SunPosition;
40+
}
41+
42+
void main()
43+
{
44+
vs_out.FragPos = geo_Pos;
45+
46+
#if defined(DYNAMIC_SUN_POSITION)
47+
vs_out.SunPosition = calculateSunPositionFromDirectionalLight();
48+
#else
49+
vs_out.SunPosition = u_SunPosition;
50+
#endif
51+
52+
gl_Position = ubo_Projection * ubo_View * vec4(ubo_ViewPos + vs_out.FragPos, 1.0);
53+
}
54+
55+
#shader fragment
56+
#version 450 core
57+
58+
#include ":Shaders/Common/Buffers/EngineUBO.ovfxh"
59+
#include ":Shaders/Common/Utils.ovfxh"
60+
61+
in VS_OUT
62+
{
63+
vec3 FragPos;
64+
flat vec3 SunPosition;
65+
} fs_in;
66+
67+
#if defined(PICKING_PASS)
68+
uniform vec4 _PickingColor;
69+
#endif
70+
71+
#if defined(OUTLINE_PASS)
72+
uniform vec4 _OutlineColor;
73+
#endif
74+
75+
uniform float u_SunIntensity = 22.0; // Sun intensity
76+
uniform float u_MiePreferredScatteringDirection = 0.758; // Mie preferred scattering direction
77+
78+
const vec3 kRayOrigin = vec3(0.0, 6372e3, 0.0); // Ray origin
79+
const float kPlanetRadius = 6371e3; // Radius of the planet in meters
80+
const float kAtmosphereRadius = 6471e3; // Radius of the atmosphere in meters
81+
const vec3 kRayleighScattering = vec3(5.5e-6, 13.0e-6, 22.4e-6); // Rayleigh scattering coefficient
82+
const float kMieScattering = 21e-6; // Mie scattering coefficient
83+
const float kRayleighScaleHeight = 8e3; // Rayleigh scale height
84+
const float kMieScaleHeight = 1.2e3; // Mie scale height
85+
86+
out vec4 FRAGMENT_COLOR;
87+
88+
// [ATMOSHPERIC SCATTERING] Section Start
89+
// From https://github.com/wwwtyro/glsl-atmosphere/blob/master/index.glsl
90+
#define PI 3.141592
91+
#define iSteps 16
92+
#define jSteps 8
93+
94+
vec2 rsi(vec3 r0, vec3 rd, float sr) {
95+
// ray-sphere intersection that assumes
96+
// the sphere is centered at the origin.
97+
// No intersection when result.x > result.y
98+
float a = dot(rd, rd);
99+
float b = 2.0 * dot(rd, r0);
100+
float c = dot(r0, r0) - (sr * sr);
101+
float d = (b*b) - 4.0*a*c;
102+
if (d < 0.0) return vec2(1e5,-1e5);
103+
return vec2(
104+
(-b - sqrt(d))/(2.0*a),
105+
(-b + sqrt(d))/(2.0*a)
106+
);
107+
}
108+
109+
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) {
110+
// Normalize the sun and view directions.
111+
pSun = normalize(pSun);
112+
r = normalize(r);
113+
114+
// Calculate the step size of the primary ray.
115+
vec2 p = rsi(r0, r, rAtmos);
116+
if (p.x > p.y) return vec3(0,0,0);
117+
p.y = min(p.y, rsi(r0, r, rPlanet).x);
118+
float iStepSize = (p.y - p.x) / float(iSteps);
119+
120+
// Initialize the primary ray time.
121+
float iTime = 0.0;
122+
123+
// Initialize accumulators for Rayleigh and Mie scattering.
124+
vec3 totalRlh = vec3(0,0,0);
125+
vec3 totalMie = vec3(0,0,0);
126+
127+
// Initialize optical depth accumulators for the primary ray.
128+
float iOdRlh = 0.0;
129+
float iOdMie = 0.0;
130+
131+
// Calculate the Rayleigh and Mie phases.
132+
float mu = dot(r, pSun);
133+
float mumu = mu * mu;
134+
float gg = g * g;
135+
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
136+
float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg));
137+
138+
// Sample the primary ray.
139+
for (int i = 0; i < iSteps; i++) {
140+
141+
// Calculate the primary ray sample position.
142+
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
143+
144+
// Calculate the height of the sample.
145+
float iHeight = length(iPos) - rPlanet;
146+
147+
// Calculate the optical depth of the Rayleigh and Mie scattering for this step.
148+
float odStepRlh = exp(-iHeight / shRlh) * iStepSize;
149+
float odStepMie = exp(-iHeight / shMie) * iStepSize;
150+
151+
// Accumulate optical depth.
152+
iOdRlh += odStepRlh;
153+
iOdMie += odStepMie;
154+
155+
// Calculate the step size of the secondary ray.
156+
float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps);
157+
158+
// Initialize the secondary ray time.
159+
float jTime = 0.0;
160+
161+
// Initialize optical depth accumulators for the secondary ray.
162+
float jOdRlh = 0.0;
163+
float jOdMie = 0.0;
164+
165+
// Sample the secondary ray.
166+
for (int j = 0; j < jSteps; j++) {
167+
168+
// Calculate the secondary ray sample position.
169+
vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5);
170+
171+
// Calculate the height of the sample.
172+
float jHeight = length(jPos) - rPlanet;
173+
174+
// Accumulate the optical depth.
175+
jOdRlh += exp(-jHeight / shRlh) * jStepSize;
176+
jOdMie += exp(-jHeight / shMie) * jStepSize;
177+
178+
// Increment the secondary ray time.
179+
jTime += jStepSize;
180+
}
181+
182+
// Calculate attenuation.
183+
vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh)));
184+
185+
// Accumulate scattering.
186+
totalRlh += odStepRlh * attn;
187+
totalMie += odStepMie * attn;
188+
189+
// Increment the primary ray time.
190+
iTime += iStepSize;
191+
192+
}
193+
194+
// Calculate and return the final color.
195+
return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie);
196+
}
197+
// [ATMOSHPERIC SCATTERING] Section End
198+
199+
void main()
200+
{
201+
#if defined(PICKING_PASS)
202+
// The atmosphere cannot be picked.
203+
discard;
204+
#elif defined(OUTLINE_PASS)
205+
// The atmosphere cannot be outlined.
206+
discard;
207+
#else
208+
209+
vec3 color = atmosphere(
210+
normalize(fs_in.FragPos), // normalized ray direction
211+
kRayOrigin, // ray origin
212+
fs_in.SunPosition, // position of the sun
213+
u_SunIntensity, // intensity of the sun
214+
kPlanetRadius, // radius of the planet in meters
215+
kAtmosphereRadius, // radius of the atmosphere in meters
216+
kRayleighScattering, // Rayleigh scattering coefficient
217+
kMieScattering, // Mie scattering coefficient
218+
kRayleighScaleHeight, // Rayleigh scale height
219+
kMieScaleHeight, // Mie scale height
220+
u_MiePreferredScatteringDirection // Mie preferred scattering direction
221+
);
222+
223+
FRAGMENT_COLOR = vec4(color, 1.0);
224+
#endif
225+
}

Resources/Engine/Shaders/Skysphere.ovfx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ out VS_OUT
1818

1919
void main()
2020
{
21-
vs_out.FragPos = ubo_ViewPos + geo_Pos * 1000.0f;
21+
vs_out.FragPos = geo_Pos;
2222
vs_out.TexCoords = geo_TexCoords;
23-
24-
gl_Position = ubo_Projection * ubo_View * vec4(vs_out.FragPos, 1.0);
23+
gl_Position = ubo_Projection * ubo_View * vec4(ubo_ViewPos + vs_out.FragPos, 1.0);
2524
}
2625

2726
#shader fragment

Sources/Overload/OvCore/include/OvCore/Rendering/PostProcess/AutoExposureEffect.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,8 @@ namespace OvCore::Rendering::PostProcess
6262
OvRendering::Data::Material m_luminanceMaterial;
6363
OvRendering::Data::Material m_exposureMaterial;
6464
OvRendering::Data::Material m_compensationMaterial;
65+
66+
// Used to skip the first frame of the exposure adaptation
67+
bool m_firstFrame = true;
6568
};
6669
}

Sources/Overload/OvCore/include/OvCore/Rendering/SceneRenderer.h

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,46 @@ namespace OvCore::Rendering
2828
class SceneRenderer : public OvRendering::Core::CompositeRenderer
2929
{
3030
public:
31-
using OpaqueDrawables = std::multimap<float, OvRendering::Entities::Drawable, std::less<float>>;
32-
using TransparentDrawables = std::multimap<float, OvRendering::Entities::Drawable, std::greater<float>>;
33-
using UIDrawables = std::multimap<float, OvRendering::Entities::Drawable, std::greater<float>>;
31+
enum class EOrderingMode
32+
{
33+
BACK_TO_FRONT,
34+
FRONT_TO_BACK,
35+
};
36+
37+
template<EOrderingMode OrderingMode>
38+
struct DrawOrder
39+
{
40+
const int order;
41+
const float distance;
42+
43+
/**
44+
* Determines the order of the drawables.
45+
* Current order is: order -> distance
46+
* @param p_other
47+
*/
48+
bool operator<(const DrawOrder& p_other) const
49+
{
50+
if (order == p_other.order)
51+
{
52+
if constexpr (OrderingMode == EOrderingMode::BACK_TO_FRONT)
53+
{
54+
return distance > p_other.distance;
55+
}
56+
else
57+
{
58+
return distance < p_other.distance;
59+
}
60+
}
61+
else
62+
{
63+
return order < p_other.order;
64+
}
65+
}
66+
};
67+
68+
using OpaqueDrawables = std::multimap<DrawOrder<EOrderingMode::FRONT_TO_BACK>, OvRendering::Entities::Drawable>;
69+
using TransparentDrawables = std::multimap<DrawOrder<EOrderingMode::BACK_TO_FRONT>, OvRendering::Entities::Drawable>;
70+
using UIDrawables = std::multimap<DrawOrder<EOrderingMode::BACK_TO_FRONT>, OvRendering::Entities::Drawable>;
3471

3572
struct AllDrawables
3673
{

Sources/Overload/OvCore/include/OvCore/SceneSystem/Scene.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ namespace OvCore::SceneSystem
6565
*/
6666
void AddDefaultSkysphere();
6767

68+
/**
69+
* Add default atmosphere to the scene
70+
*/
71+
void AddDefaultAtmosphere();
72+
6873
/**
6974
* Play the scene
7075
*/

Sources/Overload/OvCore/src/OvCore/Rendering/PostProcess/AutoExposureEffect.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ void OvCore::Rendering::PostProcess::AutoExposureEffect::Draw(
8181
m_exposureMaterial.SetProperty("_MaxLuminanceEV", autoExposureSettings.maxLuminanceEV, true);
8282
m_exposureMaterial.SetProperty("_ExposureCompensationEV", autoExposureSettings.exposureCompensationEV, true);
8383
m_exposureMaterial.SetProperty("_ElapsedTime", elapsedTime, true);
84-
m_exposureMaterial.SetProperty("_Progressive", static_cast<int>(autoExposureSettings.progressive), true);
84+
m_exposureMaterial.SetProperty("_Progressive", static_cast<int>(autoExposureSettings.progressive && !m_firstFrame), true);
8585
m_exposureMaterial.SetProperty("_SpeedUp", autoExposureSettings.speedUp, true);
8686
m_exposureMaterial.SetProperty("_SpeedDown", autoExposureSettings.speedDown, true);
8787
m_renderer.Blit(p_pso, previousExposure, currentExposure, m_exposureMaterial);
@@ -90,4 +90,6 @@ void OvCore::Rendering::PostProcess::AutoExposureEffect::Draw(
9090
const auto exposureTex = currentExposure.GetAttachment<OvRendering::HAL::Texture>(OvRendering::Settings::EFramebufferAttachment::COLOR);
9191
m_compensationMaterial.SetProperty("_ExposureTexture", &exposureTex.value(), true);
9292
m_renderer.Blit(p_pso, p_src, p_dst, m_compensationMaterial);
93+
94+
m_firstFrame = false;
9395
}

0 commit comments

Comments
 (0)