Skip to content

Commit 23ecd74

Browse files
committed
Adds volumetric fog shadow prototype - approach can be compared to the effect seen in Crysis 3.
* Adds inscattering as a rendering property (default is enabled) * Adds some minor other rendering related cleanups and tweaks
1 parent 0d1f2cc commit 23ecd74

File tree

7 files changed

+190
-34
lines changed

7 files changed

+190
-34
lines changed

src/main/java/org/terasology/config/RenderingConfig.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public class RenderingConfig {
6262
private boolean renderNearest = true;
6363
private int particleEffectLimit = 10;
6464
private int meshLimit = 20;
65+
private boolean volumetricLighting = false;
66+
private boolean inscattering = true;
6567

6668
private RenderingDebugConfig debug = new RenderingDebugConfig();
6769

@@ -316,7 +318,7 @@ public void setDynamicShadowsPcfFiltering(boolean dynamicShadowsPcfFiltering) {
316318
}
317319

318320
public boolean isVolumetricFog() {
319-
return volumetricFog;
321+
return this.volumetricFog;
320322
}
321323

322324
public void setVolumetricFog(boolean volumetricFog) {
@@ -331,6 +333,22 @@ public void setCloudShadows(boolean cloudShadows) {
331333
this.cloudShadows = cloudShadows;
332334
}
333335

336+
public boolean isVolumetricLighting() {
337+
return this.volumetricLighting;
338+
}
339+
340+
public void setVolumetricLighting(boolean volumetricLighting) {
341+
this.volumetricLighting = volumetricLighting;
342+
}
343+
344+
public boolean isInscattering() {
345+
return this.inscattering;
346+
}
347+
348+
public void setInscattering(boolean inscattering) {
349+
this.inscattering = inscattering;
350+
}
351+
334352
public boolean isRenderNearest() {
335353
return renderNearest;
336354
}

src/main/java/org/terasology/rendering/opengl/DefaultRenderingProcess.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -775,8 +775,10 @@ public void beginRenderSceneSky() {
775775
public void endRenderSceneSky() {
776776
setRenderBufferMask(true, true, true);
777777

778-
generateSkyBand(0);
779-
generateSkyBand(1);
778+
if (config.getRendering().isInscattering()) {
779+
generateSkyBand(0);
780+
generateSkyBand(1);
781+
}
780782

781783
bindFbo("sceneOpaque");
782784
}

src/main/java/org/terasology/rendering/opengl/GLSLShader.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ private static StringBuilder createShaderBuilder() {
244244
if (config.getRendering().isCloudShadows()) {
245245
builder.append("#define CLOUD_SHADOWS \n");
246246
}
247+
if (config.getRendering().isVolumetricLighting()) {
248+
builder.append("#define VOLUMETRIC_LIGHTING \n");
249+
}
250+
if (config.getRendering().isInscattering()) {
251+
builder.append("#define INSCATTERING \n");
252+
}
247253

248254
for (RenderingDebugConfig.DebugRenderingStage stage : RenderingDebugConfig.DebugRenderingStage.values()) {
249255
builder.append("#define ").append(stage.getDefineName()).append(" int(").append(stage.getIndex()).append(") \n");

src/main/java/org/terasology/rendering/shader/ShaderParametersCombine.java

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,25 @@
1515
*/
1616
package org.terasology.rendering.shader;
1717

18+
import org.lwjgl.opengl.GL11;
1819
import org.lwjgl.opengl.GL13;
20+
import org.terasology.asset.Assets;
1921
import org.terasology.config.Config;
2022
import org.terasology.editor.EditorRange;
2123
import org.terasology.engine.CoreRegistry;
2224
import org.terasology.rendering.assets.material.Material;
25+
import org.terasology.rendering.assets.texture.Texture;
2326
import org.terasology.rendering.cameras.Camera;
2427
import org.terasology.rendering.opengl.DefaultRenderingProcess;
2528
import org.terasology.rendering.world.WorldRenderer;
2629
import org.terasology.world.WorldProvider;
2730

31+
import javax.vecmath.Matrix4f;
2832
import javax.vecmath.Vector3f;
2933
import javax.vecmath.Vector4f;
3034

35+
import static org.lwjgl.opengl.GL11.glBindTexture;
36+
3137
/**
3238
* Shader parameters for the Combine shader program.
3339
*
@@ -53,6 +59,15 @@ public class ShaderParametersCombine extends ShaderParametersBase {
5359
@EditorRange(min = 0.01f, max = 1.0f)
5460
private float volFogHeightFalloff = 0.05f;
5561

62+
@EditorRange(min = 0.01f, max = 1.0f)
63+
private float volLightingDensity = 0.5f;
64+
@EditorRange(min = 0.001f, max = 0.1f)
65+
private float volLightingDecay = 0.005f;
66+
@EditorRange(min = -1.0f, max = -0.8f)
67+
private float volLightingScattering = -0.9f;
68+
@EditorRange(min = 0.0f, max = 10000.0f)
69+
private float volLightingPhi = 1000.0f;
70+
5671
@Override
5772
public void applyParameters(Material program) {
5873
super.applyParameters(program);
@@ -83,18 +98,57 @@ public void applyParameters(Material program) {
8398
Camera activeCamera = CoreRegistry.get(WorldRenderer.class).getActiveCamera();
8499
if (activeCamera != null) {
85100
program.setMatrix4("invViewProjMatrix", activeCamera.getInverseViewProjectionMatrix(), true);
101+
}
102+
103+
Vector3f fogWorldPosition = new Vector3f(0.0f, 32.0f - activeCamera.getPosition().y, 0.0f);
104+
program.setFloat3("fogWorldPosition", fogWorldPosition.x, fogWorldPosition.y, fogWorldPosition.z, true);
86105

87-
Vector3f fogWorldPosition = new Vector3f(activeCamera.getPosition().x, 32.0f, activeCamera.getPosition().y);
88-
fogWorldPosition.sub(activeCamera.getPosition());
89-
program.setFloat3("fogWorldPosition", fogWorldPosition.x, fogWorldPosition.y, fogWorldPosition.z, true);
106+
// Fog density is set according to the fog density provided by the world
107+
// TODO: The 50% percent limit shouldn't be hardcoded
108+
final float worldFog = Math.min(CoreRegistry.get(WorldProvider.class).getFog(activeCamera.getPosition()), 0.5f);
109+
program.setFloat4("volumetricFogSettings", volFogDensityAtViewer, volFogGlobalDensity, volFogHeightFalloff, worldFog);
110+
}
90111

91-
// Fog density is set according to the fog density provided by the world
92-
// TODO: The 50% percent limit shouldn't be hardcoded
93-
final float worldFog = Math.min(CoreRegistry.get(WorldProvider.class).getFog(activeCamera.getPosition()), 0.5f);
94-
program.setFloat4("volumetricFogSettings", volFogDensityAtViewer, volFogGlobalDensity, volFogHeightFalloff, worldFog);
112+
if (CoreRegistry.get(Config.class).getRendering().isVolumetricFog()
113+
|| CoreRegistry.get(Config.class).getRendering().isVolumetricLighting()) {
114+
Camera activeCamera = CoreRegistry.get(WorldRenderer.class).getActiveCamera();
115+
if (activeCamera != null) {
116+
program.setMatrix4("invViewProjMatrix", activeCamera.getInverseViewProjectionMatrix(), true);
95117
}
118+
}
96119

120+
if (CoreRegistry.get(Config.class).getRendering().isVolumetricLighting()) {
121+
GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
122+
DefaultRenderingProcess.getInstance().bindFboDepthTexture("sceneShadowMap");
123+
program.setInt("texSceneShadowMap", texId++, true);
97124

125+
Camera activeCamera = CoreRegistry.get(WorldRenderer.class).getActiveCamera();
126+
Camera lightCamera = CoreRegistry.get(WorldRenderer.class).getLightCamera();
127+
if (lightCamera != null && activeCamera != null) {
128+
program.setMatrix4("lightViewMatrix", lightCamera.getViewMatrix(), true);
129+
program.setMatrix4("lightProjMatrix", lightCamera.getProjectionMatrix(), true);
130+
program.setMatrix4("lightViewProjMatrix", lightCamera.getViewProjectionMatrix(), true);
131+
132+
program.setMatrix4("viewMatrix", activeCamera.getViewMatrix(), true);
133+
134+
Matrix4f invViewMatrix = new Matrix4f();
135+
invViewMatrix.invert(activeCamera.getViewMatrix());
136+
137+
program.setMatrix4("invViewMatrix", invViewMatrix, true);
138+
139+
Vector3f activeCameraToLightSpace = new Vector3f();
140+
activeCameraToLightSpace.sub(activeCamera.getPosition(), lightCamera.getPosition());
141+
program.setFloat3("activeCameraToLightSpace", activeCameraToLightSpace.x, activeCameraToLightSpace.y, activeCameraToLightSpace.z, true);
142+
}
143+
144+
program.setFloat4("volumetricLightingSettings", volLightingDensity, volLightingDecay, volLightingPhi, volLightingScattering, true);
145+
146+
if (CoreRegistry.get(Config.class).getRendering().isCloudShadows()) {
147+
Texture clouds = Assets.getTexture("engine:perlinNoiseTileable");
148+
GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
149+
glBindTexture(GL11.GL_TEXTURE_2D, clouds.getId());
150+
program.setInt("texSceneClouds", texId++, true);
151+
}
98152
}
99153

100154
DefaultRenderingProcess.FBO sceneTransparent = DefaultRenderingProcess.getInstance().getFBO("sceneTransparent");
@@ -120,14 +174,16 @@ public void applyParameters(Material program) {
120174
program.setFloat("outlineThickness", outlineThickness, true);
121175
}
122176

123-
GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
124-
DefaultRenderingProcess.getInstance().bindFboTexture("sceneSkyBand1");
125-
program.setInt("texSceneSkyBand", texId++, true);
126-
127-
Vector4f skyInscatteringSettingsFrag = new Vector4f();
128-
skyInscatteringSettingsFrag.y = skyInscatteringStrength;
129-
skyInscatteringSettingsFrag.z = skyInscatteringLength;
130-
skyInscatteringSettingsFrag.w = skyInscatteringThreshold;
131-
program.setFloat4("skyInscatteringSettingsFrag", skyInscatteringSettingsFrag, true);
177+
if (CoreRegistry.get(Config.class).getRendering().isInscattering()) {
178+
GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
179+
DefaultRenderingProcess.getInstance().bindFboTexture("sceneSkyBand1");
180+
program.setInt("texSceneSkyBand", texId++, true);
181+
182+
Vector4f skyInscatteringSettingsFrag = new Vector4f();
183+
skyInscatteringSettingsFrag.y = skyInscatteringStrength;
184+
skyInscatteringSettingsFrag.z = skyInscatteringLength;
185+
skyInscatteringSettingsFrag.w = skyInscatteringThreshold;
186+
program.setFloat4("skyInscatteringSettingsFrag", skyInscatteringSettingsFrag, true);
187+
}
132188
}
133189
}

src/main/resources/assets/shaders/combine_frag.glsl

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ uniform sampler2D texSceneOpaqueNormals;
2020
uniform sampler2D texSceneOpaqueLightBuffer;
2121
uniform sampler2D texSceneTransparent;
2222

23+
#ifdef INSCATTERING
2324
uniform vec4 skyInscatteringSettingsFrag;
2425
#define skyInscatteringStrength skyInscatteringSettingsFrag.y
2526
#define skyInscatteringLength skyInscatteringSettingsFrag.z
2627
#define skyInscatteringThreshold skyInscatteringSettingsFrag.w
2728

2829
uniform sampler2D texSceneSkyBand;
30+
#endif
2931

3032
#ifdef SSAO
3133
uniform sampler2D texSsao;
@@ -39,7 +41,7 @@ uniform float outlineThickness;
3941
# define OUTLINE_COLOR 0.0, 0.0, 0.0
4042
#endif
4143

42-
#if defined (VOLUMETRIC_FOG)
44+
#if defined (VOLUMETRIC_FOG) || defined (VOLUMETRIC_LIGHTING)
4345
uniform mat4 invViewProjMatrix;
4446
#endif
4547

@@ -55,14 +57,36 @@ uniform vec4 volumetricFogSettings;
5557
uniform vec3 fogWorldPosition;
5658
#endif
5759

60+
#if defined (VOLUMETRIC_LIGHTING)
61+
#define SAMPLES 300.0
62+
#define STARTING_POINT viewingDistance // defines the amount of samples used / the starting point
63+
#define STEP_SIZE STARTING_POINT / SAMPLES
64+
65+
uniform vec4 volumetricLightingSettings;
66+
67+
uniform mat4 lightViewMatrix;
68+
uniform mat4 lightProjMatrix;
69+
uniform mat4 lightViewProjMatrix;
70+
71+
uniform mat4 viewMatrix;
72+
uniform mat4 invViewMatrix;
73+
74+
uniform sampler2D texSceneShadowMap;
75+
uniform vec3 activeCameraToLightSpace;
76+
77+
#if defined (CLOUD_SHADOWS)
78+
uniform sampler2D texSceneClouds;
79+
#endif
80+
#endif
81+
5882
void main() {
5983
vec4 colorOpaque = texture2D(texSceneOpaque, gl_TexCoord[0].xy);
6084
float depthOpaque = texture2D(texSceneOpaqueDepth, gl_TexCoord[0].xy).r * 2.0 - 1.0;
6185
vec4 normalsOpaque = texture2D(texSceneOpaqueNormals, gl_TexCoord[0].xy);
6286
vec4 colorTransparent = texture2D(texSceneTransparent, gl_TexCoord[0].xy);
6387
vec4 lightBufferOpaque = texture2D(texSceneOpaqueLightBuffer, gl_TexCoord[0].xy);
6488

65-
#if defined (VOLUMETRIC_FOG)
89+
#if defined (VOLUMETRIC_FOG) || defined (VOLUMETRIC_LIGHTING)
6690
// TODO: As costly as in the deferred light geometry pass - frustum ray method would be great here
6791
vec3 worldPosition = reconstructViewPos(depthOpaque, gl_TexCoord[0].xy, invViewProjMatrix);
6892
#endif
@@ -80,6 +104,7 @@ void main() {
80104
colorOpaque.rgb = mix(colorOpaque.rgb, vec3(OUTLINE_COLOR), outline);
81105
#endif
82106

107+
#if defined (INSCATTERING)
83108
// Sky inscattering using down-sampled sky band texture
84109
vec3 skyInscatteringColor = texture2D(texSceneSkyBand, gl_TexCoord[0].xy).rgb;
85110

@@ -91,13 +116,67 @@ void main() {
91116
colorOpaque.rgb = mix(colorOpaque.rgb, skyInscatteringColor, fogValue);
92117
colorTransparent.rgb = mix(colorTransparent.rgb, skyInscatteringColor, fogValue);
93118
}
119+
#endif
120+
121+
#if defined (VOLUMETRIC_LIGHTING)
122+
vec2 projectedPos = gl_TexCoord[0].xy;
123+
124+
// Guess a position for fragments that have been projected to the far plane (e.g. the sky)
125+
float len = length(worldPosition.xyz);
126+
worldPosition.xyz = clamp(len, 0.0, viewingDistance) * (worldPosition.xyz / len);
127+
128+
vec3 lightWorldSpaceVertPos = worldPosition.xyz + activeCameraToLightSpace;
129+
130+
// float L = volumetricLightingL0 * exp(-STARTING_POINT * volumetricLightingTau);
131+
float L = 0.0;
132+
133+
vec4 lightViewSpaceVertPos = lightViewMatrix * vec4(lightWorldSpaceVertPos.x, lightWorldSpaceVertPos.y, lightWorldSpaceVertPos.z, 1.0);
134+
vec3 viewDir = normalize(-lightViewSpaceVertPos.xyz);
135+
vec4 viewSpacePosition = lightViewSpaceVertPos;
136+
137+
for (float l = STARTING_POINT - STEP_SIZE; l >= 0; l -= STEP_SIZE) {
138+
viewSpacePosition.xyz += vec3(STEP_SIZE, STEP_SIZE, STEP_SIZE) * viewDir.xyz;
139+
140+
vec4 screenSpacePosition = lightProjMatrix * viewSpacePosition;
141+
screenSpacePosition.xyz /= screenSpacePosition.w;
142+
screenSpacePosition.xy = screenSpacePosition.xy * vec2(0.5, 0.5) + vec2(0.5, 0.5);
143+
144+
float sd = texture2D(texSceneShadowMap, screenSpacePosition.xy).x;
145+
float v = (sd < screenSpacePosition.z) ? 0.0 : volumetricLightingSettings.x;
146+
147+
#if defined (CLOUD_SHADOWS)
148+
// Modulate volumetric lighting with some fictional cloud shadows
149+
v *= clamp(1.0 - texture2D(texSceneClouds, screenSpacePosition.xy * 0.5 + timeToTick(time, 0.002)).r * 5.0, 0.0, 1.0);
150+
#endif
151+
152+
float d = clamp(length(viewSpacePosition.xyz), 10.0, viewingDistance);
153+
float cosTheta = dot(normalize(-viewSpacePosition.xyz), -viewDir);
154+
float g = volumetricLightingSettings.w;
155+
float phi = volumetricLightingSettings.z;
156+
157+
float henyeyGreenstein = (1.0 / (4.0 * PI)) * (1.0 - g) * (1.0 - g);
158+
henyeyGreenstein /= pow(1.0 + g*g - 2.0 * g * cosTheta, 3.0 / 2.0);
159+
160+
float intens = henyeyGreenstein * volumetricLightingSettings.y * (v * phi/4.0/PI/d/d) * exp(-d * volumetricLightingSettings.y) * exp(-l * volumetricLightingSettings.y) * STEP_SIZE;
161+
162+
L += intens;
163+
};
164+
165+
colorOpaque.rgb += vec3(L, L, L);
166+
colorTransparent.rgb += vec3(L, L, L);
167+
#endif
94168

95169
#if defined (VOLUMETRIC_FOG)
96170
// Use lightValueAtPlayerPos to avoid volumetric fog in caves
97-
float volumetricFogValue = volFogDensity *sunlightValueAtPlayerPos *
171+
float volumetricFogValue = volFogDensity * sunlightValueAtPlayerPos *
98172
calcVolumetricFog(worldPosition - fogWorldPosition, volFogDensityAtViewer, volFogGlobalDensity, volFogHeightFalloff);
99173

100-
vec3 volFogColor = skyInscatteringColor * vec3(VOLUMETRIC_FOG_COLOR);
174+
vec3 volFogColor =
175+
#if defined (INSCATTERING)
176+
skyInscatteringColor *
177+
#endif
178+
vec3(VOLUMETRIC_FOG_COLOR);
179+
101180
colorOpaque.rgb = mix(colorOpaque.rgb, volFogColor, volumetricFogValue);
102181
colorTransparent.rgb = mix(colorTransparent.rgb, volFogColor, volumetricFogValue);
103182
#endif

src/main/resources/assets/shaders/lightGeometryPass_frag.glsl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,24 @@ void main() {
7070
vec3 worldPosition = reconstructViewPos(depth, gl_TexCoord[0].xy, invViewProjMatrix);
7171
vec3 lightWorldPosition = worldPosition.xyz + activeCameraToLightSpace;
7272

73-
vec4 lightProjPos = lightViewProjMatrix * vec4(lightWorldPosition.x, lightWorldPosition.y, lightWorldPosition.z, 1.0);
73+
vec4 lightProjVertPos = lightViewProjMatrix * vec4(lightWorldPosition.x, lightWorldPosition.y, lightWorldPosition.z, 1.0);
7474

75-
vec3 lightPosClipSpace = lightProjPos.xyz / lightProjPos.w;
76-
vec2 shadowMapTexPos = lightPosClipSpace.xy * vec2(0.5) + vec2(0.5);
75+
lightProjVertPos.xyz /= lightProjVertPos.w;
76+
vec2 shadowMapTexPos = lightProjVertPos.xy * vec2(0.5) + vec2(0.5);
7777

7878
float shadowTerm = 1.0;
7979

8080
if (!epsilonEqualsOne(depth)) {
8181
#if defined (DYNAMIC_SHADOWS_PCF)
82-
shadowTerm = calcPcfShadowTerm(texSceneShadowMap, lightPosClipSpace.z, shadowMapTexPos, 0.0, SHADOW_MAP_BIAS);
82+
shadowTerm = calcPcfShadowTerm(texSceneShadowMap, lightProjVertPos.z, shadowMapTexPos, 0.0, SHADOW_MAP_BIAS);
8383
#else
8484
float shadowMapDepth = texture2D(texSceneShadowMap, shadowMapTexPos).x;
85-
if (shadowMapDepth + SHADOW_MAP_BIAS < lightPosClipSpace.z) {
85+
if (shadowMapDepth + SHADOW_MAP_BIAS < lightProjVertPos.z) {
8686
shadowTerm = 0.0;
8787
}
8888
#endif
8989

90-
#if defined (CLOUD_SHADOWS)
90+
#if defined (CLOUD_SHADOWS) && !defined (VOLUMETRIC_LIGHTING)
9191
// TODO: Add shader parameters for this...
9292
float cloudOcclusion = clamp(1.0 - texture2D(texSceneClouds, (worldPosition.xz + cameraPosition.xz) * 0.005 + timeToTick(time, 0.004)).r * 5.0, 0.0, 1.0);
9393
shadowTerm *= clamp(1.0 - cloudOcclusion + 0.25, 0.0, 1.0);

src/main/resources/assets/shaders/lightshaft_frag.glsl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,6 @@ void main() {
3333

3434
float sceneDepth = texture2D(texDepth, texCoord).r * 2.0 - 1.0;
3535

36-
// Only blur the sky
37-
// if (!epsilonEqualsOne(sceneDepth)) {
38-
// discard;
39-
// }
40-
4136
vec2 deltaTexCoord = vec2(texCoord - lightScreenPos.xy);
4237

4338
deltaTexCoord *= (1.0 / float(LIGHT_SHAFT_SAMPLES)) * density;

0 commit comments

Comments
 (0)