Skip to content

Commit

Permalink
Adds volumetric fog shadow prototype - approach can be compared to th…
Browse files Browse the repository at this point in the history
…e effect seen in Crysis 3.

* Adds inscattering as a rendering property (default is enabled)
* Adds some minor other rendering related cleanups and tweaks
  • Loading branch information
begla committed Sep 29, 2013
1 parent 0d1f2cc commit 23ecd74
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 34 deletions.
20 changes: 19 additions & 1 deletion src/main/java/org/terasology/config/RenderingConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public class RenderingConfig {
private boolean renderNearest = true;
private int particleEffectLimit = 10;
private int meshLimit = 20;
private boolean volumetricLighting = false;
private boolean inscattering = true;

private RenderingDebugConfig debug = new RenderingDebugConfig();

Expand Down Expand Up @@ -316,7 +318,7 @@ public void setDynamicShadowsPcfFiltering(boolean dynamicShadowsPcfFiltering) {
}

public boolean isVolumetricFog() {
return volumetricFog;
return this.volumetricFog;
}

public void setVolumetricFog(boolean volumetricFog) {
Expand All @@ -331,6 +333,22 @@ public void setCloudShadows(boolean cloudShadows) {
this.cloudShadows = cloudShadows;
}

public boolean isVolumetricLighting() {
return this.volumetricLighting;
}

public void setVolumetricLighting(boolean volumetricLighting) {
this.volumetricLighting = volumetricLighting;
}

public boolean isInscattering() {
return this.inscattering;
}

public void setInscattering(boolean inscattering) {
this.inscattering = inscattering;
}

public boolean isRenderNearest() {
return renderNearest;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -775,8 +775,10 @@ public void beginRenderSceneSky() {
public void endRenderSceneSky() {
setRenderBufferMask(true, true, true);

generateSkyBand(0);
generateSkyBand(1);
if (config.getRendering().isInscattering()) {
generateSkyBand(0);
generateSkyBand(1);
}

bindFbo("sceneOpaque");
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/terasology/rendering/opengl/GLSLShader.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,12 @@ private static StringBuilder createShaderBuilder() {
if (config.getRendering().isCloudShadows()) {
builder.append("#define CLOUD_SHADOWS \n");
}
if (config.getRendering().isVolumetricLighting()) {
builder.append("#define VOLUMETRIC_LIGHTING \n");
}
if (config.getRendering().isInscattering()) {
builder.append("#define INSCATTERING \n");
}

for (RenderingDebugConfig.DebugRenderingStage stage : RenderingDebugConfig.DebugRenderingStage.values()) {
builder.append("#define ").append(stage.getDefineName()).append(" int(").append(stage.getIndex()).append(") \n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,25 @@
*/
package org.terasology.rendering.shader;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.terasology.asset.Assets;
import org.terasology.config.Config;
import org.terasology.editor.EditorRange;
import org.terasology.engine.CoreRegistry;
import org.terasology.rendering.assets.material.Material;
import org.terasology.rendering.assets.texture.Texture;
import org.terasology.rendering.cameras.Camera;
import org.terasology.rendering.opengl.DefaultRenderingProcess;
import org.terasology.rendering.world.WorldRenderer;
import org.terasology.world.WorldProvider;

import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4f;

import static org.lwjgl.opengl.GL11.glBindTexture;

/**
* Shader parameters for the Combine shader program.
*
Expand All @@ -53,6 +59,15 @@ public class ShaderParametersCombine extends ShaderParametersBase {
@EditorRange(min = 0.01f, max = 1.0f)
private float volFogHeightFalloff = 0.05f;

@EditorRange(min = 0.01f, max = 1.0f)
private float volLightingDensity = 0.5f;
@EditorRange(min = 0.001f, max = 0.1f)
private float volLightingDecay = 0.005f;
@EditorRange(min = -1.0f, max = -0.8f)
private float volLightingScattering = -0.9f;
@EditorRange(min = 0.0f, max = 10000.0f)
private float volLightingPhi = 1000.0f;

@Override
public void applyParameters(Material program) {
super.applyParameters(program);
Expand Down Expand Up @@ -83,18 +98,57 @@ public void applyParameters(Material program) {
Camera activeCamera = CoreRegistry.get(WorldRenderer.class).getActiveCamera();
if (activeCamera != null) {
program.setMatrix4("invViewProjMatrix", activeCamera.getInverseViewProjectionMatrix(), true);
}

Vector3f fogWorldPosition = new Vector3f(0.0f, 32.0f - activeCamera.getPosition().y, 0.0f);
program.setFloat3("fogWorldPosition", fogWorldPosition.x, fogWorldPosition.y, fogWorldPosition.z, true);

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

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

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

Camera activeCamera = CoreRegistry.get(WorldRenderer.class).getActiveCamera();
Camera lightCamera = CoreRegistry.get(WorldRenderer.class).getLightCamera();
if (lightCamera != null && activeCamera != null) {
program.setMatrix4("lightViewMatrix", lightCamera.getViewMatrix(), true);
program.setMatrix4("lightProjMatrix", lightCamera.getProjectionMatrix(), true);
program.setMatrix4("lightViewProjMatrix", lightCamera.getViewProjectionMatrix(), true);

program.setMatrix4("viewMatrix", activeCamera.getViewMatrix(), true);

Matrix4f invViewMatrix = new Matrix4f();
invViewMatrix.invert(activeCamera.getViewMatrix());

program.setMatrix4("invViewMatrix", invViewMatrix, true);

Vector3f activeCameraToLightSpace = new Vector3f();
activeCameraToLightSpace.sub(activeCamera.getPosition(), lightCamera.getPosition());
program.setFloat3("activeCameraToLightSpace", activeCameraToLightSpace.x, activeCameraToLightSpace.y, activeCameraToLightSpace.z, true);
}

program.setFloat4("volumetricLightingSettings", volLightingDensity, volLightingDecay, volLightingPhi, volLightingScattering, true);

if (CoreRegistry.get(Config.class).getRendering().isCloudShadows()) {
Texture clouds = Assets.getTexture("engine:perlinNoiseTileable");
GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
glBindTexture(GL11.GL_TEXTURE_2D, clouds.getId());
program.setInt("texSceneClouds", texId++, true);
}
}

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

GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
DefaultRenderingProcess.getInstance().bindFboTexture("sceneSkyBand1");
program.setInt("texSceneSkyBand", texId++, true);

Vector4f skyInscatteringSettingsFrag = new Vector4f();
skyInscatteringSettingsFrag.y = skyInscatteringStrength;
skyInscatteringSettingsFrag.z = skyInscatteringLength;
skyInscatteringSettingsFrag.w = skyInscatteringThreshold;
program.setFloat4("skyInscatteringSettingsFrag", skyInscatteringSettingsFrag, true);
if (CoreRegistry.get(Config.class).getRendering().isInscattering()) {
GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
DefaultRenderingProcess.getInstance().bindFboTexture("sceneSkyBand1");
program.setInt("texSceneSkyBand", texId++, true);

Vector4f skyInscatteringSettingsFrag = new Vector4f();
skyInscatteringSettingsFrag.y = skyInscatteringStrength;
skyInscatteringSettingsFrag.z = skyInscatteringLength;
skyInscatteringSettingsFrag.w = skyInscatteringThreshold;
program.setFloat4("skyInscatteringSettingsFrag", skyInscatteringSettingsFrag, true);
}
}
}
87 changes: 83 additions & 4 deletions src/main/resources/assets/shaders/combine_frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ uniform sampler2D texSceneOpaqueNormals;
uniform sampler2D texSceneOpaqueLightBuffer;
uniform sampler2D texSceneTransparent;

#ifdef INSCATTERING
uniform vec4 skyInscatteringSettingsFrag;
#define skyInscatteringStrength skyInscatteringSettingsFrag.y
#define skyInscatteringLength skyInscatteringSettingsFrag.z
#define skyInscatteringThreshold skyInscatteringSettingsFrag.w

uniform sampler2D texSceneSkyBand;
#endif

#ifdef SSAO
uniform sampler2D texSsao;
Expand All @@ -39,7 +41,7 @@ uniform float outlineThickness;
# define OUTLINE_COLOR 0.0, 0.0, 0.0
#endif

#if defined (VOLUMETRIC_FOG)
#if defined (VOLUMETRIC_FOG) || defined (VOLUMETRIC_LIGHTING)
uniform mat4 invViewProjMatrix;
#endif

Expand All @@ -55,14 +57,36 @@ uniform vec4 volumetricFogSettings;
uniform vec3 fogWorldPosition;
#endif

#if defined (VOLUMETRIC_LIGHTING)
#define SAMPLES 300.0
#define STARTING_POINT viewingDistance // defines the amount of samples used / the starting point
#define STEP_SIZE STARTING_POINT / SAMPLES

uniform vec4 volumetricLightingSettings;

uniform mat4 lightViewMatrix;
uniform mat4 lightProjMatrix;
uniform mat4 lightViewProjMatrix;

uniform mat4 viewMatrix;
uniform mat4 invViewMatrix;

uniform sampler2D texSceneShadowMap;
uniform vec3 activeCameraToLightSpace;

#if defined (CLOUD_SHADOWS)
uniform sampler2D texSceneClouds;
#endif
#endif

void main() {
vec4 colorOpaque = texture2D(texSceneOpaque, gl_TexCoord[0].xy);
float depthOpaque = texture2D(texSceneOpaqueDepth, gl_TexCoord[0].xy).r * 2.0 - 1.0;
vec4 normalsOpaque = texture2D(texSceneOpaqueNormals, gl_TexCoord[0].xy);
vec4 colorTransparent = texture2D(texSceneTransparent, gl_TexCoord[0].xy);
vec4 lightBufferOpaque = texture2D(texSceneOpaqueLightBuffer, gl_TexCoord[0].xy);

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

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

Expand All @@ -91,13 +116,67 @@ void main() {
colorOpaque.rgb = mix(colorOpaque.rgb, skyInscatteringColor, fogValue);
colorTransparent.rgb = mix(colorTransparent.rgb, skyInscatteringColor, fogValue);
}
#endif

#if defined (VOLUMETRIC_LIGHTING)
vec2 projectedPos = gl_TexCoord[0].xy;

// Guess a position for fragments that have been projected to the far plane (e.g. the sky)
float len = length(worldPosition.xyz);
worldPosition.xyz = clamp(len, 0.0, viewingDistance) * (worldPosition.xyz / len);

vec3 lightWorldSpaceVertPos = worldPosition.xyz + activeCameraToLightSpace;

// float L = volumetricLightingL0 * exp(-STARTING_POINT * volumetricLightingTau);
float L = 0.0;

vec4 lightViewSpaceVertPos = lightViewMatrix * vec4(lightWorldSpaceVertPos.x, lightWorldSpaceVertPos.y, lightWorldSpaceVertPos.z, 1.0);
vec3 viewDir = normalize(-lightViewSpaceVertPos.xyz);
vec4 viewSpacePosition = lightViewSpaceVertPos;

for (float l = STARTING_POINT - STEP_SIZE; l >= 0; l -= STEP_SIZE) {
viewSpacePosition.xyz += vec3(STEP_SIZE, STEP_SIZE, STEP_SIZE) * viewDir.xyz;

vec4 screenSpacePosition = lightProjMatrix * viewSpacePosition;
screenSpacePosition.xyz /= screenSpacePosition.w;
screenSpacePosition.xy = screenSpacePosition.xy * vec2(0.5, 0.5) + vec2(0.5, 0.5);

float sd = texture2D(texSceneShadowMap, screenSpacePosition.xy).x;
float v = (sd < screenSpacePosition.z) ? 0.0 : volumetricLightingSettings.x;

#if defined (CLOUD_SHADOWS)
// Modulate volumetric lighting with some fictional cloud shadows
v *= clamp(1.0 - texture2D(texSceneClouds, screenSpacePosition.xy * 0.5 + timeToTick(time, 0.002)).r * 5.0, 0.0, 1.0);
#endif

float d = clamp(length(viewSpacePosition.xyz), 10.0, viewingDistance);
float cosTheta = dot(normalize(-viewSpacePosition.xyz), -viewDir);
float g = volumetricLightingSettings.w;
float phi = volumetricLightingSettings.z;

float henyeyGreenstein = (1.0 / (4.0 * PI)) * (1.0 - g) * (1.0 - g);
henyeyGreenstein /= pow(1.0 + g*g - 2.0 * g * cosTheta, 3.0 / 2.0);

float intens = henyeyGreenstein * volumetricLightingSettings.y * (v * phi/4.0/PI/d/d) * exp(-d * volumetricLightingSettings.y) * exp(-l * volumetricLightingSettings.y) * STEP_SIZE;

L += intens;
};

colorOpaque.rgb += vec3(L, L, L);
colorTransparent.rgb += vec3(L, L, L);
#endif

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

vec3 volFogColor = skyInscatteringColor * vec3(VOLUMETRIC_FOG_COLOR);
vec3 volFogColor =
#if defined (INSCATTERING)
skyInscatteringColor *
#endif
vec3(VOLUMETRIC_FOG_COLOR);

colorOpaque.rgb = mix(colorOpaque.rgb, volFogColor, volumetricFogValue);
colorTransparent.rgb = mix(colorTransparent.rgb, volFogColor, volumetricFogValue);
#endif
Expand Down
12 changes: 6 additions & 6 deletions src/main/resources/assets/shaders/lightGeometryPass_frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,24 @@ void main() {
vec3 worldPosition = reconstructViewPos(depth, gl_TexCoord[0].xy, invViewProjMatrix);
vec3 lightWorldPosition = worldPosition.xyz + activeCameraToLightSpace;

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

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

float shadowTerm = 1.0;

if (!epsilonEqualsOne(depth)) {
#if defined (DYNAMIC_SHADOWS_PCF)
shadowTerm = calcPcfShadowTerm(texSceneShadowMap, lightPosClipSpace.z, shadowMapTexPos, 0.0, SHADOW_MAP_BIAS);
shadowTerm = calcPcfShadowTerm(texSceneShadowMap, lightProjVertPos.z, shadowMapTexPos, 0.0, SHADOW_MAP_BIAS);
#else
float shadowMapDepth = texture2D(texSceneShadowMap, shadowMapTexPos).x;
if (shadowMapDepth + SHADOW_MAP_BIAS < lightPosClipSpace.z) {
if (shadowMapDepth + SHADOW_MAP_BIAS < lightProjVertPos.z) {
shadowTerm = 0.0;
}
#endif

#if defined (CLOUD_SHADOWS)
#if defined (CLOUD_SHADOWS) && !defined (VOLUMETRIC_LIGHTING)
// TODO: Add shader parameters for this...
float cloudOcclusion = clamp(1.0 - texture2D(texSceneClouds, (worldPosition.xz + cameraPosition.xz) * 0.005 + timeToTick(time, 0.004)).r * 5.0, 0.0, 1.0);
shadowTerm *= clamp(1.0 - cloudOcclusion + 0.25, 0.0, 1.0);
Expand Down
5 changes: 0 additions & 5 deletions src/main/resources/assets/shaders/lightshaft_frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ void main() {

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

// Only blur the sky
// if (!epsilonEqualsOne(sceneDepth)) {
// discard;
// }

vec2 deltaTexCoord = vec2(texCoord - lightScreenPos.xy);

deltaTexCoord *= (1.0 / float(LIGHT_SHAFT_SAMPLES)) * density;
Expand Down

0 comments on commit 23ecd74

Please sign in to comment.