Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg
+++ binaries/data/config/default.cfg
@@ -69,9 +69,10 @@
shadowsonwater = false
shadows = true
-shadowquality = 0 ; Shadow map resolution. (-2 - Very Low, -1 - Low, 0 - Medium, 1 - High, 2 - Very High)
- ; High values can crash the game when using a graphics card with low memory!
-shadowpcf = true
+shadowquality = 1 ; Shadow map resolution. (0 - Low, 1 - Medium, 2 - High, 3 - Ultra)
+ ; High values can break shadows when using a graphics card with low memory!
+shadowpcf = true ; Basic shadow filtering.
+shadowpcss = false ; High quality (and expensive) variable penumbra shadows.
vsync = false
particles = true
fog = true
@@ -111,6 +112,8 @@
; Use screen-space postprocessing filters (HDR, bloom, DOF, etc). Incompatible with fixed renderpath.
postproc = false
+dof = false ; Use depth of field effect.
+
; Quality level of shader effects (set to 10 to display all effects)
materialmgr.quality = 2.0
Index: binaries/data/mods/public/gui/options/options.json
===================================================================
--- binaries/data/mods/public/gui/options/options.json
+++ binaries/data/mods/public/gui/options/options.json
@@ -58,6 +58,13 @@
"config": "gui.session.ceasefirecounter"
},
{
+ "type": "boolean",
+ "label": "Unit Silhouettes",
+ "tooltip": "Show outlines of units behind buildings.",
+ "config": "silhouettes",
+ "function": "Renderer_SetSilhouettesEnabled"
+ },
+ {
"type": "dropdown",
"label": "Late Observer Joins",
"tooltip": "Allow everybody or buddies only to join the game as observer after it started.",
@@ -97,16 +104,8 @@
},
{
"type": "boolean",
- "label": "Prefer GLSL",
- "tooltip": "Use OpenGL 2.0 shaders (recommended).",
- "config": "preferglsl",
- "function": "Renderer_SetPreferGLSLEnabled"
- },
- {
- "type": "boolean",
"label": "Fog",
"tooltip": "Enable Fog.",
- "dependencies": ["preferglsl"],
"config": "fog",
"function": "Renderer_SetFogEnabled"
},
@@ -118,6 +117,14 @@
"function": "Renderer_SetPostprocEnabled"
},
{
+ "type": "boolean",
+ "label": "Depth of Field",
+ "tooltip": "Apply the depth of field postprocessing effect.",
+ "dependencies": ["postproc"],
+ "config": "dof",
+ "function": "Renderer_SetDOFEnabled"
+ },
+ {
"type": "slider",
"label": "Shader Effects",
"tooltip": "Number of shader effects. REQUIRES GAME RESTART",
@@ -135,16 +142,15 @@
{
"type": "dropdown",
"label": "Shadow Quality",
- "tooltip": "Shadow map resolution. High values can crash the game when using a graphics card with low memory!",
+ "tooltip": "Shadow map resolution.",
"dependencies": ["shadows"],
"config": "shadowquality",
"function": "Renderer_RecreateShadowMap",
"list": [
- { "value": -2, "label": "Very Low" },
- { "value": -1, "label": "Low" },
- { "value": 0, "label": "Medium" },
- { "value": 1, "label": "High" },
- { "value": 2, "label": "Very High" }
+ { "value": 0, "label": "Low" },
+ { "value": 1, "label": "Medium" },
+ { "value": 2, "label": "High" },
+ { "value": 3, "label": "Ultra" }
]
},
{
@@ -157,10 +163,11 @@
},
{
"type": "boolean",
- "label": "Unit Silhouettes",
- "tooltip": "Show outlines of units behind buildings.",
- "config": "silhouettes",
- "function": "Renderer_SetSilhouettesEnabled"
+ "label": "Soft Shadows",
+ "tooltip": "Variable penumbra shadows. (Expensive)",
+ "dependencies": ["shadows", "shadowpcf"],
+ "config": "shadowpcss",
+ "function": "Renderer_SetShadowPCSSEnabled"
},
{
"type": "boolean",
Index: binaries/data/mods/public/shaders/effects/postproc/DOF.xml
===================================================================
--- binaries/data/mods/public/shaders/effects/postproc/DOF.xml
+++ binaries/data/mods/public/shaders/effects/postproc/DOF.xml
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
Index: binaries/data/mods/public/shaders/effects/postproc/HDR.xml
===================================================================
--- binaries/data/mods/public/shaders/effects/postproc/HDR.xml
+++ binaries/data/mods/public/shaders/effects/postproc/HDR.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
Index: binaries/data/mods/public/shaders/effects/postproc/hdr.xml
===================================================================
--- binaries/data/mods/public/shaders/effects/postproc/hdr.xml
+++ binaries/data/mods/public/shaders/effects/postproc/hdr.xml
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
Index: binaries/data/mods/public/shaders/glsl/bloom.fs
===================================================================
--- binaries/data/mods/public/shaders/glsl/bloom.fs
+++ binaries/data/mods/public/shaders/glsl/bloom.fs
@@ -1,41 +1,37 @@
-#version 110
+#version 120
varying vec2 v_tex;
uniform sampler2D renderedTex;
uniform vec2 texSize;
+#if BIG_BLUR
+// 5 tap linear sampled gaussian blur, created using BlurNinja: https://github.com/manuelbua/blur-ninja
+uniform float weight[2] = float[](0.375, 0.3125);
+uniform float offset = 1.2;
+#else
+// 3 tap gaussian blur
+uniform float weight[2] = float[](0.5, 0.25);
+uniform float offset = 1.0;
+#endif
+
void main()
{
- #if BLOOM_NOP
- gl_FragColor = texture2D(renderedTex, v_tex);
- gl_FragColor.a = 1.0;
- #endif
+#if BLOOM_NOP
+ gl_FragColor = texture2D(renderedTex, v_tex);
+ gl_FragColor.a = 1.0;
+#endif
- #if BLOOM_PASS_H
- vec4 color = vec4(0.0);
- vec2 v_tex_offs = vec2(v_tex.x - 0.01, v_tex.y);
-
- for (int i = 0; i < 6; ++i)
- {
- color += texture2D(renderedTex, v_tex_offs);
- v_tex_offs += vec2(0.004, 0.0);
- }
-
- gl_FragColor.rgb = color.rgb / 6.0;
- gl_FragColor.a = 1.0;
- #endif
+#if BLOOM_PASS_H
+ gl_FragColor = texture2D(renderedTex, gl_FragCoord.xy / texSize) * weight[0];
+ gl_FragColor += texture2D(renderedTex, (gl_FragCoord.xy + vec2(offset, 0.0))/texSize) * weight[1];
+ gl_FragColor += texture2D(renderedTex, (gl_FragCoord.xy - vec2(offset, 0.0))/texSize) * weight[1];
+ gl_FragColor.a = 1.0;
+#endif
- #if BLOOM_PASS_V
- vec4 color = vec4(0.0);
- vec2 v_tex_offs = vec2(v_tex.x, v_tex.y - 0.01);
-
- for (int i = 0; i < 6; ++i)
- {
- color += texture2D(renderedTex, v_tex_offs);
- v_tex_offs += vec2(0.0, 0.004);
- }
-
- gl_FragColor.rgb = color.rgb / 6.0;
- gl_FragColor.a = 1.0;
- #endif
+#if BLOOM_PASS_V
+ gl_FragColor = texture2D(renderedTex, gl_FragCoord.xy / texSize) * weight[0];
+ gl_FragColor += texture2D(renderedTex, ((gl_FragCoord.xy + vec2(0.0, offset))/texSize)) * weight[1];
+ gl_FragColor += texture2D(renderedTex, ((gl_FragCoord.xy - vec2(0.0, offset))/texSize)) * weight[1];
+ gl_FragColor.a = 1.0;
+#endif
}
\ No newline at end of file
Index: binaries/data/mods/public/shaders/glsl/bloom.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/bloom.xml
+++ binaries/data/mods/public/shaders/glsl/bloom.xml
@@ -1,7 +1,7 @@
-
+
Index: binaries/data/mods/public/shaders/glsl/dof_hdr.fs
===================================================================
--- binaries/data/mods/public/shaders/glsl/dof_hdr.fs
+++ binaries/data/mods/public/shaders/glsl/dof_hdr.fs
@@ -0,0 +1,71 @@
+#version 120
+
+uniform sampler2D renderedTex;
+uniform sampler2D depthTex;
+uniform sampler2D downscaleTex2;
+uniform sampler2D blurTex2;
+uniform sampler2D blurTex4;
+uniform sampler2D blurTex8;
+
+uniform float width;
+uniform float height;
+
+uniform float zNear;
+uniform float zFar;
+
+uniform float brightness;
+uniform float contrast;
+uniform float saturation;
+uniform float bloom;
+
+varying vec2 v_tex;
+
+float linearizeDepth(float depth)
+{
+ return 2.0 * zNear / (zNear + zFar - depth * (zFar - zNear));
+}
+
+void main(void)
+{
+ vec3 color = texture2D(renderedTex, v_tex).rgb;
+ vec3 blur2 = texture2D(blurTex2, v_tex).rgb;
+
+#if USE_DOF
+ // Apply Depth of Field
+ vec3 down2 = texture2D(downscaleTex2, v_tex).rgb;
+
+ float depth = linearizeDepth(texture2D(depthTex, v_tex).r);
+ float amount = pow(depth, 1.25) * 6.5; // Using a semi-exponential ramp to keep nearby objects from being semi-blurred.
+
+ if (amount < 1.0){
+ color = mix(color, down2, amount);
+ }else{
+ amount = clamp(amount - 1.0, 0.0, 1.0);
+ color = mix(down2, blur2, amount);
+ }
+#endif
+
+#if USE_HDR
+ // Apply bloom and color adjustments
+ if (bloom < 0.2){
+ // Note: 0.2 is the "zero" setting for bloom for some unfathomable reason.
+ // If bloom is set to "zero" we skip all the texture reads and operations so as not to waste GPU cycles.
+ vec3 blur4 = texture2D(blurTex4, v_tex).rgb;
+ vec3 blur8 = texture2D(blurTex8, v_tex).rgb;
+ vec3 blur = (blur2 + blur4 + blur8)/3.0;
+
+ blur = mix(color, blur, (1.0 - (bloom * 5.0)));
+
+ color = max(blur, color);
+ }
+
+ color = (color + brightness - 0.5) * contrast + 0.5;
+
+ color = mix(vec3(dot(color, vec3(0.299, 0.587, 0.114))), color, saturation);
+#endif
+
+ gl_FragColor.rgb = color;
+ gl_FragColor.a = 1.0;
+}
+
+
Index: binaries/data/mods/public/shaders/glsl/dof_hdr.vs
===================================================================
--- binaries/data/mods/public/shaders/glsl/dof_hdr.vs
+++ binaries/data/mods/public/shaders/glsl/dof_hdr.vs
@@ -0,0 +1,13 @@
+#version 110
+
+varying vec2 v_tex;
+
+attribute vec3 a_vertex;
+attribute vec2 a_uv0;
+
+void main()
+{
+ gl_Position = vec4(a_vertex, 1.0);
+
+ v_tex = a_uv0;
+}
Index: binaries/data/mods/public/shaders/glsl/dof_hdr.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/dof_hdr.xml
+++ binaries/data/mods/public/shaders/glsl/dof_hdr.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: binaries/data/mods/public/shaders/glsl/model_common.fs
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_common.fs
+++ binaries/data/mods/public/shaders/glsl/model_common.fs
@@ -7,25 +7,45 @@
uniform sampler2D specTex;
#if USE_SHADOW
- varying vec4 v_shadow;
- #if USE_SHADOW_SAMPLER
- uniform sampler2DShadow shadowTex;
- #if USE_SHADOW_PCF
- uniform vec4 shadowScale;
- #endif
- #else
- uniform sampler2D shadowTex;
- #endif
+varying vec4 v_shadow;
+#if USE_SHADOW_SAMPLER
+uniform sampler2DShadow shadowTex;
+#if USE_SHADOW_PCF || USE_SHADOW_PCSS
+uniform vec4 shadowScale;
+// shadowScale.xy gives the dimensionless scale factor based on the shadow map resolution and the shadow frustum x and y bounds.
+// shadowScale.z gives the dimensionless relative z bounds of the shadow frustum.
+// shadowScale.w gives 1.0 divided by the shadow map length in texels, for tex coord scaling.
#endif
+#if USE_SHADOW_PCSS
+ uniform sampler2D blockerTex;
+#endif
+#else
+uniform sampler2D shadowTex;
+#endif
+#endif
#if USE_OBJECTCOLOR
- uniform vec3 objectColor;
+uniform vec3 objectColor;
#else
#if USE_PLAYERCOLOR
- uniform vec3 playerColor;
+uniform vec3 playerColor;
#endif
#endif
+#if USE_SHADOW_PCF || (USE_SHADOW_PCSS && USE_TRANSPARENT)
+// This is a 'random' poisson disk with a uniform distribution created with a poisson disk generator.
+uniform vec2 offsets[8] = vec2[](
+ vec2(-0.353553, 0.612372),
+ vec2(-0.25, -0.433013),
+ vec2(0.663414, 0.55667),
+ vec2(-0.332232, 0.120922),
+ vec2(0.137281, -0.778559),
+ vec2(0.106337, 0.603069),
+ vec2(-0.879002, -0.319931),
+ vec2(0.191511, -0.160697)
+);
+#endif
+
uniform vec3 shadingColor;
uniform vec3 ambient;
uniform vec3 sunColor;
@@ -39,58 +59,113 @@
varying vec2 v_los;
#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_AO
- varying vec2 v_tex2;
+varying vec2 v_tex2;
#endif
#if USE_SPECULAR
- uniform float specularPower;
- uniform vec3 specularColor;
+uniform float specularPower;
+uniform vec3 specularColor;
#endif
#if USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_PARALLAX || USE_AO
- uniform vec4 effectSettings;
+uniform vec4 effectSettings;
#endif
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_PARALLAX
- varying vec4 v_normal;
- #if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_NORMAL_MAP || USE_PARALLAX)
- varying vec4 v_tangent;
- //varying vec3 v_bitangent;
- #endif
- #if USE_SPECULAR || USE_SPECULAR_MAP
- varying vec3 v_half;
- #endif
- #if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
- varying vec3 v_eyeVec;
- #endif
+varying vec4 v_normal;
+#if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_NORMAL_MAP || USE_PARALLAX)
+varying vec4 v_tangent;
+//varying vec3 v_bitangent;
#endif
+#if USE_SPECULAR || USE_SPECULAR_MAP
+varying vec3 v_half;
+#endif
+#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
+varying vec3 v_eyeVec;
+#endif
+#endif
float get_shadow()
{
- float shadowBias = 0.003;
- #if USE_SHADOW && !DISABLE_RECEIVE_SHADOWS
- float biasedShdwZ = v_shadow.z - shadowBias;
- #if USE_SHADOW_SAMPLER
- #if USE_SHADOW_PCF
- vec2 offset = fract(v_shadow.xy - 0.5);
- vec4 size = vec4(offset + 1.0, 2.0 - offset);
- vec4 weight = (vec4(1.0, 1.0, -0.5, -0.5) + (v_shadow.xy - 0.5*offset).xyxy) * shadowScale.zwzw;
- return (1.0/9.0)*dot(size.zxzx*size.wwyy,
- vec4(shadow2D(shadowTex, vec3(weight.zw, biasedShdwZ)).r,
- shadow2D(shadowTex, vec3(weight.xw, biasedShdwZ)).r,
- shadow2D(shadowTex, vec3(weight.zy, biasedShdwZ)).r,
- shadow2D(shadowTex, vec3(weight.xy, biasedShdwZ)).r));
- #else
- return shadow2D(shadowTex, vec3(v_shadow.xy, biasedShdwZ)).r;
- #endif
- #else
- if (biasedShdwZ >= 1.0)
- return 1.0;
- return (biasedShdwZ < texture2D(shadowTex, v_shadow.xy).x ? 1.0 : 0.0);
- #endif
- #else
+ float shadowBias = 0.003;
+#if USE_SHADOW && !DISABLE_RECEIVE_SHADOWS
+float biasedShdwZ = v_shadow.z - shadowBias;
+#if USE_SHADOW_SAMPLER
+#if USE_SHADOW_PCF || (USE_SHADOW_PCSS && USE_TRANSPARENT)
+ float blurRadius = 3.0;
+ const int numSamples = 8;
+ float lit = 0.0;
+ vec2 uv = v_shadow.xy - (0.5 * fract(v_shadow.xy - 0.5));
+
+ for (int i = 0; i < numSamples; ++i){
+ vec2 offset = (uv + offsets[i] * blurRadius * shadowScale.xy) * shadowScale.w;
+ lit += shadow2D(shadowTex, vec3(offset, biasedShdwZ)).r;
+ }
+
+ return lit/float(numSamples);
+#else
+#if USE_SHADOW_PCSS
+ int numSamples = 5;
+ vec2 uv = v_shadow.xy - (0.5 * fract(v_shadow.xy - 0.5));
+
+ // Perform blocker search
+ float hits = 0.0;
+ float avgdist = 0.0;
+ vec2 off = vec2(-24.5);
+ for (int i = 0; i < numSamples; ++i){
+ for (int j = 0; j < numSamples; ++j){
+ vec2 offset = (uv + vec2(off.x + 9.6 * float(j), off.y) * shadowScale.xy) * shadowScale.w;
+ float dist = texture2D(blockerTex, offset).z;
+
+ // Note: I'm converting a bool to a float here to avoid a nasty branch in the shader.
+ float hit = float(dist < biasedShdwZ);
+ hits += hit;
+ avgdist += dist * hit;
+ }
+ off.y = off.y + 9.6;
+ }
+
+ if (hits == 0.0)
+ return 1.0; // Early exit if no blockers are found.
+
+ // Perform penumbra estimation and scale the number of samples with blur size for efficiency.
+ avgdist /= hits;
+
+ float lightsize = 1.0 * shadowScale.z;
+ float penumbrascale = (biasedShdwZ - avgdist)/avgdist;
+
+ float amount = min(lightsize * penumbrascale, 1.0);
+ float sampleamount = min((lightsize/1.5) * penumbrascale, 1.0);
+
+ numSamples = int(floor(mix(2.0, 16.0, amount)));
+ float sampleScale = mix(1.0, 24.0, sampleamount); // This allows smooth transitions by varying the spacing of the samples.
+ float blurscale = sampleScale/float(numSamples);
+
+ // Perform shadow sampling
+ float lit = 0.0;
+ off = vec2(-0.5 - float(numSamples));
+ lit += shadow2D(shadowTex, vec3(uv * shadowScale.w, biasedShdwZ)).r;
+ for (int i = 0; i < numSamples; ++i){
+ for (int j = 0; j < numSamples; ++j){
+ vec2 offset = (uv + vec2(off.x + 2.0 * float(j), off.y) * blurscale * shadowScale.xy) * shadowScale.w;
+ lit += shadow2D(shadowTex, vec3(offset, biasedShdwZ)).r;
+ }
+ off.y = off.y + 2.0;
+ }
+ float shad = 1.0 - lit/(float(numSamples * numSamples) + 1.0);
+ return 1.0 - pow(shad, 1.25);
+#else
+ return shadow2D(shadowTex, vec3(v_shadow.xy, biasedShdwZ)).r;
+#endif
+#endif
+#else
+ if (biasedShdwZ >= 1.0)
+ return 1.0;
+ return (biasedShdwZ < texture2D(shadowTex, v_shadow.xy).x ? 1.0 : 0.0);
+#endif
+#else
return 1.0;
- #endif
+#endif
}
vec3 get_fog(vec3 color)
@@ -111,26 +186,25 @@
void main()
{
- vec2 coord = v_tex;
+ vec2 coord = v_tex;
- #if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_PARALLAX || USE_NORMAL_MAP)
- vec3 bitangent = vec3(v_normal.w, v_tangent.w, v_lighting.w);
- mat3 tbn = mat3(v_tangent.xyz, bitangent, v_normal.xyz);
- #endif
+#if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_PARALLAX || USE_NORMAL_MAP)
+ vec3 bitangent = vec3(v_normal.w, v_tangent.w, v_lighting.w);
+ mat3 tbn = mat3(v_tangent.xyz, bitangent, v_normal.xyz);
+#endif
- #if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
- {
- float h = texture2D(normTex, coord).a;
+#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
+ float h = texture2D(normTex, coord).a;
- vec3 eyeDir = normalize(v_eyeVec * tbn);
- float dist = length(v_eyeVec);
+ vec3 eyeDir = normalize(v_eyeVec * tbn);
+ float dist = length(v_eyeVec);
- vec2 move;
- float height = 1.0;
- float scale = effectSettings.z;
+ vec2 move;
+ float height = 1.0;
+ float scale = effectSettings.z;
- int iter = int(min(20, 25.0 - dist/10.0));
-
+ int iter = int(min(20, 25.0 - dist/10.0));
+
if (iter > 0.01)
{
float s = 1.0/iter;
@@ -152,90 +226,89 @@
float hp = texture2D(normTex, coord - move).a;
coord -= move * ((h - height) / (s + h - hp));
}
- }
- #endif
+#endif
- vec4 tex = texture2D(baseTex, coord);
+ vec4 tex = texture2D(baseTex, coord);
// Alpha-test as early as possible
- #ifdef REQUIRE_ALPHA_GEQUAL
- if (tex.a < REQUIRE_ALPHA_GEQUAL)
- discard;
- #endif
+#ifdef REQUIRE_ALPHA_GEQUAL
+ if (tex.a < REQUIRE_ALPHA_GEQUAL)
+ discard;
+#endif
- #if USE_TRANSPARENT
- gl_FragColor.a = tex.a;
- #else
- gl_FragColor.a = 1.0;
- #endif
+#if USE_TRANSPARENT
+ gl_FragColor.a = tex.a;
+#else
+ gl_FragColor.a = 1.0;
+#endif
- vec3 texdiffuse = tex.rgb;
+ vec3 texdiffuse = tex.rgb;
// Apply-coloring based on texture alpha
- #if USE_OBJECTCOLOR
- texdiffuse *= mix(objectColor, vec3(1.0, 1.0, 1.0), tex.a);
- #else
- #if USE_PLAYERCOLOR
- texdiffuse *= mix(playerColor, vec3(1.0, 1.0, 1.0), tex.a);
- #endif
- #endif
+#if USE_OBJECTCOLOR
+ texdiffuse *= mix(objectColor, vec3(1.0, 1.0, 1.0), tex.a);
+#else
+#if USE_PLAYERCOLOR
+ texdiffuse *= mix(playerColor, vec3(1.0, 1.0, 1.0), tex.a);
+#endif
+#endif
- #if USE_SPECULAR || USE_SPECULAR_MAP || USE_NORMAL_MAP
+#if USE_SPECULAR || USE_SPECULAR_MAP || USE_NORMAL_MAP
vec3 normal = v_normal.xyz;
- #endif
+#endif
- #if (USE_INSTANCING || USE_GPU_SKINNING) && USE_NORMAL_MAP
- vec3 ntex = texture2D(normTex, coord).rgb * 2.0 - 1.0;
- ntex.y = -ntex.y;
- normal = normalize(tbn * ntex);
- vec3 bumplight = max(dot(-sunDir, normal), 0.0) * sunColor;
- vec3 sundiffuse = (bumplight - v_lighting.rgb) * effectSettings.x + v_lighting.rgb;
- #else
- vec3 sundiffuse = v_lighting.rgb;
- #endif
+#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_NORMAL_MAP
+ vec3 ntex = texture2D(normTex, coord).rgb * 2.0 - 1.0;
+ ntex.y = -ntex.y;
+ normal = normalize(tbn * ntex);
+ vec3 bumplight = max(dot(-sunDir, normal), 0.0) * sunColor;
+ vec3 sundiffuse = (bumplight - v_lighting.rgb) * effectSettings.x + v_lighting.rgb;
+#else
+ vec3 sundiffuse = v_lighting.rgb;
+#endif
- vec4 specular = vec4(0.0);
- #if USE_SPECULAR || USE_SPECULAR_MAP
+ vec4 specular = vec4(0.0);
+#if USE_SPECULAR || USE_SPECULAR_MAP
vec3 specCol;
float specPow;
- #if USE_SPECULAR_MAP
- vec4 s = texture2D(specTex, coord);
- specCol = s.rgb;
- specular.a = s.a;
- specPow = effectSettings.y;
- #else
- specCol = specularColor;
- specPow = specularPower;
- #endif
+#if USE_SPECULAR_MAP
+ vec4 s = texture2D(specTex, coord);
+ specCol = s.rgb;
+ specular.a = s.a;
+ specPow = effectSettings.y;
+#else
+ specCol = specularColor;
+ specPow = specularPower;
+#endif
specular.rgb = sunColor * specCol * pow(max(0.0, dot(normalize(normal), v_half)), specPow);
- #endif
+#endif
- vec3 color = (texdiffuse * sundiffuse + specular.rgb) * get_shadow();
- vec3 ambColor = texdiffuse * ambient;
+ vec3 color = (texdiffuse * sundiffuse + specular.rgb) * get_shadow();
+ vec3 ambColor = texdiffuse * ambient;
- #if (USE_INSTANCING || USE_GPU_SKINNING) && USE_AO
+#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_AO
vec3 ao = texture2D(aoTex, v_tex2).rrr;
ao = mix(vec3(1.0), ao * 2.0, effectSettings.w);
ambColor *= ao;
- #endif
+#endif
- color += ambColor;
+ color += ambColor;
- #if USE_SPECULAR_MAP && USE_SELF_LIGHT
+#if USE_SPECULAR_MAP && USE_SELF_LIGHT
color = mix(texdiffuse, color, specular.a);
- #endif
+#endif
- #if USE_FOG
+#if USE_FOG
color = get_fog(color);
- #endif
+#endif
- #if !IGNORE_LOS
- float los = texture2D(losTex, v_los).a;
- los = los < 0.03 ? 0.0 : los;
- color *= los;
- #endif
+#if !IGNORE_LOS
+ float los = texture2D(losTex, v_los).a;
+ los = los < 0.03 ? 0.0 : los;
+ color *= los;
+#endif
- color *= shadingColor;
+ color *= shadingColor;
- gl_FragColor.rgb = color;
+ gl_FragColor.rgb = color;
}
Index: binaries/data/mods/public/shaders/glsl/model_common.vs
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_common.vs
+++ binaries/data/mods/public/shaders/glsl/model_common.vs
@@ -9,17 +9,18 @@
uniform vec3 sunDir;
uniform vec3 sunColor;
#endif
+
uniform vec2 losTransform;
uniform mat4 shadowTransform;
uniform mat4 instancingTransform;
-#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
- uniform vec4 shadowScale;
+#if USE_SHADOW_SAMPLER && (USE_SHADOW_PCF || USE_SHADOW_PCSS)
+uniform vec4 shadowScale;
#endif
#if USE_WIND
- uniform vec4 sim_time;
- uniform vec4 windData;
+uniform vec4 sim_time;
+uniform vec4 windData;
#endif
varying vec4 v_lighting;
@@ -27,41 +28,40 @@
varying vec2 v_los;
#if USE_SHADOW
- varying vec4 v_shadow;
+ varying vec4 v_shadow;
#endif
#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_AO
- varying vec2 v_tex2;
+ varying vec2 v_tex2;
#endif
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_PARALLAX
- varying vec4 v_normal;
- #if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_NORMAL_MAP || USE_PARALLAX)
- varying vec4 v_tangent;
- //varying vec3 v_bitangent;
- #endif
- #if USE_SPECULAR || USE_SPECULAR_MAP
- varying vec3 v_half;
- #endif
- #if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
- varying vec3 v_eyeVec;
- #endif
+varying vec4 v_normal;
+#if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_NORMAL_MAP || USE_PARALLAX)
+varying vec4 v_tangent;
#endif
+#if USE_SPECULAR || USE_SPECULAR_MAP
+varying vec3 v_half;
+#endif
+#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
+varying vec3 v_eyeVec;
+#endif
+#endif
attribute vec3 a_vertex;
attribute vec3 a_normal;
#if (USE_INSTANCING || USE_GPU_SKINNING)
- attribute vec4 a_tangent;
+attribute vec4 a_tangent;
#endif
attribute vec2 a_uv0;
attribute vec2 a_uv1;
#if USE_GPU_SKINNING
- const int MAX_INFLUENCES = 4;
- const int MAX_BONES = 64;
- uniform mat4 skinBlendMatrices[MAX_BONES];
- attribute vec4 a_skinJoints;
- attribute vec4 a_skinWeights;
+const int MAX_INFLUENCES = 4;
+const int MAX_BONES = 64;
+uniform mat4 skinBlendMatrices[MAX_BONES];
+attribute vec4 a_skinJoints;
+attribute vec4 a_skinWeights;
#endif
@@ -74,111 +74,112 @@
void main()
{
- #if USE_GPU_SKINNING
- vec3 p = vec3(0.0);
- vec3 n = vec3(0.0);
- for (int i = 0; i < MAX_INFLUENCES; ++i) {
- int joint = int(a_skinJoints[i]);
- if (joint != 0xff) {
- mat4 m = skinBlendMatrices[joint];
- p += vec3(m * vec4(a_vertex, 1.0)) * a_skinWeights[i];
- n += vec3(m * vec4(a_normal, 0.0)) * a_skinWeights[i];
- }
- }
- vec4 position = instancingTransform * vec4(p, 1.0);
- mat3 normalMatrix = mat3(instancingTransform[0].xyz, instancingTransform[1].xyz, instancingTransform[2].xyz);
- vec3 normal = normalMatrix * normalize(n);
- #if (USE_NORMAL_MAP || USE_PARALLAX)
- vec3 tangent = normalMatrix * a_tangent.xyz;
- #endif
- #else
- #if (USE_INSTANCING)
- vec4 position = instancingTransform * vec4(a_vertex, 1.0);
- mat3 normalMatrix = mat3(instancingTransform[0].xyz, instancingTransform[1].xyz, instancingTransform[2].xyz);
- vec3 normal = normalMatrix * a_normal;
- #if (USE_NORMAL_MAP || USE_PARALLAX)
- vec3 tangent = normalMatrix * a_tangent.xyz;
- #endif
- #else
- vec4 position = vec4(a_vertex, 1.0);
- vec3 normal = a_normal;
- #endif
- #endif
+#if USE_GPU_SKINNING
+ vec3 p = vec3(0.0);
+ vec3 n = vec3(0.0);
+ for (int i = 0; i < MAX_INFLUENCES; ++i)
+ {
+ int joint = int(a_skinJoints[i]);
+ if (joint != 0xff)
+ {
+ mat4 m = skinBlendMatrices[joint];
+ p += vec3(m * vec4(a_vertex, 1.0)) * a_skinWeights[i];
+ n += vec3(m * vec4(a_normal, 0.0)) * a_skinWeights[i];
+ }
+ }
+ vec4 position = instancingTransform * vec4(p, 1.0);
+ mat3 normalMatrix = mat3(instancingTransform[0].xyz, instancingTransform[1].xyz, instancingTransform[2].xyz);
+ vec3 normal = normalMatrix * normalize(n);
+#if (USE_NORMAL_MAP || USE_PARALLAX)
+ vec3 tangent = normalMatrix * a_tangent.xyz;
+#endif
+#else
+#if (USE_INSTANCING)
+ vec4 position = instancingTransform * vec4(a_vertex, 1.0);
+ mat3 normalMatrix = mat3(instancingTransform[0].xyz, instancingTransform[1].xyz, instancingTransform[2].xyz);
+ vec3 normal = normalMatrix * a_normal;
+#if (USE_NORMAL_MAP || USE_PARALLAX)
+ vec3 tangent = normalMatrix * a_tangent.xyz;
+#endif
+#else
+ vec4 position = vec4(a_vertex, 1.0);
+ vec3 normal = a_normal;
+#endif
+#endif
- #if USE_WIND
- vec2 wind = windData.xy;
+#if USE_WIND
+ vec2 wind = windData.xy;
- // fractional part of model position, clamped to >.4
- vec4 modelPos = instancingTransform[3];
- modelPos = fract(modelPos);
- modelPos = clamp(modelPos, 0.4, 1.0);
+ // fractional part of model position, clamped to >.4
+ vec4 modelPos = instancingTransform[3];
+ modelPos = fract(modelPos);
+ modelPos = clamp(modelPos, 0.4, 1.0);
- // crude measure of wind intensity
- float abswind = abs(wind.x) + abs(wind.y);
+ // crude measure of wind intensity
+ float abswind = abs(wind.x) + abs(wind.y);
- vec4 cosVec;
- // these determine the speed of the wind's "cosine" waves.
- cosVec.w = 0.0;
- cosVec.x = sim_time.x * modelPos[0] + position.x;
- cosVec.y = sim_time.x * modelPos[2] / 3.0 + instancingTransform[3][0];
- cosVec.z = sim_time.x * abswind / 4.0 + position.z;
+ vec4 cosVec;
+ // these determine the speed of the wind's "cosine" waves.
+ cosVec.w = 0.0;
+ cosVec.x = sim_time.x * modelPos[0] + position.x;
+ cosVec.y = sim_time.x * modelPos[2] / 3.0 + instancingTransform[3][0];
+ cosVec.z = sim_time.x * abswind / 4.0 + position.z;
- // calculate "cosines" in parallel, using a smoothed triangle wave
- cosVec = fakeCos(cosVec);
+ // calculate "cosines" in parallel, using a smoothed triangle wave
+ cosVec = fakeCos(cosVec);
- float limit = clamp((a_vertex.x * a_vertex.z * a_vertex.y) / 3000.0, 0.0, 0.2);
+ float limit = clamp((a_vertex.x * a_vertex.z * a_vertex.y) / 3000.0, 0.0, 0.2);
- float diff = cosVec.x * limit;
- float diff2 = cosVec.y * clamp(a_vertex.y / 60.0, 0.0, 0.25);
+ float diff = cosVec.x * limit;
+ float diff2 = cosVec.y * clamp(a_vertex.y / 60.0, 0.0, 0.25);
- // fluttering of model parts based on distance from model center (ie longer branches)
- position.xyz += cosVec.z * limit * clamp(abswind, 1.2, 1.7);
+ // fluttering of model parts based on distance from model center (ie longer branches)
+ position.xyz += cosVec.z * limit * clamp(abswind, 1.2, 1.7);
- // swaying of trunk based on distance from ground (higher parts sway more)
- position.xz += diff + diff2 * wind;
- #endif
+ // swaying of trunk based on distance from ground (higher parts sway more)
+ position.xz += diff + diff2 * wind;
+#endif
+ gl_Position = transform * position;
- gl_Position = transform * position;
+#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_PARALLAX
+ v_normal.xyz = normal;
- #if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_PARALLAX
- v_normal.xyz = normal;
+#if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_NORMAL_MAP || USE_PARALLAX)
+ v_tangent.xyz = tangent;
+ vec3 bitangent = cross(v_normal.xyz, v_tangent.xyz) * a_tangent.w;
+ v_normal.w = bitangent.x;
+ v_tangent.w = bitangent.y;
+ v_lighting.w = bitangent.z;
+#endif
- #if (USE_INSTANCING || USE_GPU_SKINNING) && (USE_NORMAL_MAP || USE_PARALLAX)
- v_tangent.xyz = tangent;
- vec3 bitangent = cross(v_normal.xyz, v_tangent.xyz) * a_tangent.w;
- v_normal.w = bitangent.x;
- v_tangent.w = bitangent.y;
- v_lighting.w = bitangent.z;
- #endif
-
- #if USE_SPECULAR || USE_SPECULAR_MAP || USE_PARALLAX
- vec3 eyeVec = cameraPos.xyz - position.xyz;
- #if USE_SPECULAR || USE_SPECULAR_MAP
- vec3 sunVec = -sunDir;
- v_half = normalize(sunVec + normalize(eyeVec));
- #endif
- #if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
- v_eyeVec = eyeVec;
- #endif
- #endif
- #endif
+#if USE_SPECULAR || USE_SPECULAR_MAP || USE_PARALLAX
+ vec3 eyeVec = cameraPos.xyz - position.xyz;
+#if USE_SPECULAR || USE_SPECULAR_MAP
+ vec3 sunVec = -sunDir;
+ v_half = normalize(sunVec + normalize(eyeVec));
+#endif
+#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_PARALLAX
+ v_eyeVec = eyeVec;
+#endif
+#endif
+#endif
- v_lighting.xyz = max(0.0, dot(normal, -sunDir)) * sunColor;
+ v_lighting.xyz = max(0.0, dot(normal, -sunDir)) * sunColor;
- v_tex = a_uv0;
+ v_tex = a_uv0;
- #if (USE_INSTANCING || USE_GPU_SKINNING) && USE_AO
- v_tex2 = a_uv1;
- #endif
+#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_AO
+ v_tex2 = a_uv1;
+#endif
- #if USE_SHADOW
- v_shadow = shadowTransform * position;
- #if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
- v_shadow.xy *= shadowScale.xy;
- #endif
- #endif
+#if USE_SHADOW
+ v_shadow = shadowTransform * position;
+#if USE_SHADOW_SAMPLER && (USE_SHADOW_PCF || USE_SHADOW_PCSS)
+ v_shadow.xy /= shadowScale.w;
+#endif
+#endif
- v_los = position.xz * losTransform.x + losTransform.y;
+ v_los = position.xz * losTransform.x + losTransform.y;
}
Index: binaries/data/mods/public/shaders/glsl/terrain_common.fs
===================================================================
--- binaries/data/mods/public/shaders/glsl/terrain_common.fs
+++ binaries/data/mods/public/shaders/glsl/terrain_common.fs
@@ -7,16 +7,21 @@
uniform sampler2D specTex;
#if USE_SHADOW
- uniform float shadowAngle;
- #if USE_SHADOW_SAMPLER
- uniform sampler2DShadow shadowTex;
- #if USE_SHADOW_PCF
- uniform vec4 shadowScale;
- #endif
- #else
- uniform sampler2D shadowTex;
- #endif
+#if USE_SHADOW_SAMPLER
+uniform sampler2DShadow shadowTex;
+#if USE_SHADOW_PCF || USE_SHADOW_PCSS
+uniform vec4 shadowScale;
+// shadowScale.xy gives the dimensionless scale factor based on the shadow map resolution and the shadow frustum x and y bounds.
+// shadowScale.z gives the dimensionless relative z bounds of the shadow frustum.
+// shadowScale.w gives 1.0 divided by the shadow map length in texels, for tex coord scaling.
#endif
+#if USE_SHADOW_PCSS
+uniform sampler2D blockerTex;
+#endif
+#else
+uniform sampler2D shadowTex;
+#endif
+#endif
uniform vec3 shadingColor;
uniform vec3 ambient;
@@ -31,7 +36,7 @@
varying vec3 v_lighting;
#if USE_SHADOW
- varying vec4 v_shadow;
+varying vec4 v_shadow;
#endif
varying vec2 v_los;
@@ -38,58 +43,135 @@
varying vec2 v_blend;
#if USE_TRIPLANAR
- varying vec3 v_tex;
+varying vec3 v_tex;
#else
- varying vec2 v_tex;
+varying vec2 v_tex;
#endif
#if USE_SPECULAR
- uniform float specularPower;
- uniform vec3 specularColor;
+uniform float specularPower;
+uniform vec3 specularColor;
#endif
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_AO
- uniform vec4 effectSettings;
+uniform vec4 effectSettings;
#endif
varying vec3 v_normal;
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP
- #if USE_NORMAL_MAP
- varying vec4 v_tangent;
- varying vec3 v_bitangent;
- #endif
- #if USE_SPECULAR || USE_SPECULAR_MAP
- varying vec3 v_half;
- #endif
+#if USE_NORMAL_MAP
+varying vec4 v_tangent;
+varying vec3 v_bitangent;
#endif
+#if USE_SPECULAR || USE_SPECULAR_MAP
+varying vec3 v_half;
+#endif
+#endif
+#if USE_SHADOW_PCF || USE_SHADOW_PCSS
+// This is a 'random' poisson disk with a uniform distribution created with a poisson disk generator.
+uniform vec2 offsets[16] = vec2[](
+ vec2(-0.353553, 0.612372),
+ vec2(-0.25, -0.433013),
+ vec2(0.663414, 0.55667),
+ vec2(-0.332232, 0.120922),
+ vec2(0.137281, -0.778559),
+ vec2(0.106337, 0.603069),
+ vec2(-0.879002, -0.319931),
+ vec2(0.191511, -0.160697),
+ vec2(0.729784, 0.172962),
+ vec2(-0.383621, 0.406614),
+ vec2(-0.258521, -0.86352),
+ vec2(0.258577, 0.34733),
+ vec2(-0.82355, 0.0962588),
+ vec2(0.261982, -0.607343),
+ vec2(-0.0562987, 0.966608),
+ vec2(-0.147695, -0.0971404)
+);
+#endif
+
float get_shadow()
{
- float shadowBias = 0.0005;
- #if USE_SHADOW && !DISABLE_RECEIVE_SHADOWS
- float biasedShdwZ = v_shadow.z - shadowBias;
- #if USE_SHADOW_SAMPLER
- #if USE_SHADOW_PCF
- vec2 offset = fract(v_shadow.xy - 0.5);
- vec4 size = vec4(offset + 1.0, 2.0 - offset);
- vec4 weight = (vec4(1.0, 1.0, -0.5, -0.5) + (v_shadow.xy - 0.5*offset).xyxy) * shadowScale.zwzw;
- return (1.0/9.0)*dot(size.zxzx*size.wwyy,
- vec4(shadow2D(shadowTex, vec3(weight.zw, biasedShdwZ)).r,
- shadow2D(shadowTex, vec3(weight.xw, biasedShdwZ)).r,
- shadow2D(shadowTex, vec3(weight.zy, biasedShdwZ)).r,
- shadow2D(shadowTex, vec3(weight.xy, biasedShdwZ)).r));
- #else
- return shadow2D(shadowTex, vec3(v_shadow.xy, biasedShdwZ)).r;
- #endif
- #else
- if (biasedShdwZ >= 1.0)
- return 1.0;
- return (biasedShdwZ < texture2D(shadowTex, v_shadow.xy).x ? 1.0 : 0.0);
- #endif
- #else
+ float shadowBias = 0.0005;
+#if USE_SHADOW && !DISABLE_RECEIVE_SHADOWS
+ float biasedShdwZ = v_shadow.z - shadowBias;
+#if USE_SHADOW_SAMPLER
+#if USE_SHADOW_PCF
+ float blurRadius = 3.0;
+ const int numSamples = 16;
+ float lit = 0.0;
+ vec2 uv = v_shadow.xy - (0.5 * fract(v_shadow.xy - 0.5));
+
+ for (int i = 0; i < numSamples; ++i){
+ vec2 offset = (uv + offsets[i] * blurRadius * shadowScale.xy) * shadowScale.w;
+ lit += shadow2D(shadowTex, vec3(offset, biasedShdwZ)).r;
+ }
+
+ return lit/float(numSamples);
+#else
+#if USE_SHADOW_PCSS
+ int numSamples = 5;
+ vec2 uv = v_shadow.xy - (0.5 * fract(v_shadow.xy - 0.5));
+
+ // Perform blocker search
+ float hits = 0.0;
+ float avgdist = 0.0;
+ vec2 off = vec2(-24.5);
+ for (int i = 0; i < numSamples; ++i){
+ for (int j = 0; j < numSamples; ++j){
+ vec2 offset = (uv + vec2(off.x + 9.6 * float(j), off.y) * shadowScale.xy) * shadowScale.w;
+ float dist = texture2D(blockerTex, offset).z;
+
+ // Note: I'm converting a bool to a float here to avoid a nasty branch in the shader.
+ float hit = float(dist < biasedShdwZ);
+ hits += hit;
+ avgdist += dist * hit;
+ }
+ off.y = off.y + 9.6;
+ }
+
+ if (hits == 0.0)
+ return 1.0; // Early exit if no blockers are found.
+
+ // Perform penumbra estimation and scale the number of samples with blur size for efficiency.
+ avgdist /= hits;
+
+ float lightsize = 1.0 * shadowScale.z;
+ float penumbrascale = (biasedShdwZ - avgdist)/avgdist;
+
+ float amount = min(lightsize * penumbrascale, 1.0);
+ float sampleamount = min((lightsize/1.5) * penumbrascale, 1.0);
+
+ numSamples = int(floor(mix(2.0, 16.0, amount)));
+ float sampleScale = mix(1.0, 24.0, sampleamount); // This allows smooth transitions by varying the spacing of the samples.
+ float blurscale = sampleScale/float(numSamples);
+
+ // Perform shadow sampling
+ float lit = 0.0;
+ off = vec2(-0.5 - float(numSamples));
+ lit += shadow2D(shadowTex, vec3(uv * shadowScale.w, biasedShdwZ)).r;
+ for (int i = 0; i < numSamples; ++i){
+ for (int j = 0; j < numSamples; ++j){
+ vec2 offset = (uv + vec2(off.x + 2.0 * float(j), off.y) * blurscale * shadowScale.xy) * shadowScale.w;
+ lit += shadow2D(shadowTex, vec3(offset, biasedShdwZ)).r;
+ }
+ off.y = off.y + 2.0;
+ }
+ float shad = 1.0 - lit/(float(numSamples * numSamples) + 1.0);
+ return 1.0 - pow(shad, 1.25);
+#else
+ return shadow2D(shadowTex, vec3(v_shadow.xy, biasedShdwZ)).r;
+#endif
+#endif
+#else
+ if (biasedShdwZ >= 1.0)
+ return 1.0;
+ return (biasedShdwZ < texture2D(shadowTex, v_shadow.xy).x ? 1.0 : 0.0);
+#endif
+#else
return 1.0;
- #endif
+#endif
}
#if USE_TRIPLANAR
@@ -161,96 +243,96 @@
void main()
{
- #if BLEND
- // Use alpha from blend texture
- gl_FragColor.a = 1.0 - texture2D(blendTex, v_blend).a;
+#if BLEND
+ // Use alpha from blend texture
+ gl_FragColor.a = 1.0 - texture2D(blendTex, v_blend).a;
- #if USE_GRASS
- if (gl_FragColor.a < LAYER / 10.0)
- discard;
- #endif
- #else
- gl_FragColor.a = 1.0;
- #endif
+#if USE_GRASS
+ if (gl_FragColor.a < LAYER / 10.0)
+ discard;
+#endif
+#else
+ gl_FragColor.a = 1.0;
+#endif
- #if USE_TRIPLANAR
- vec4 tex = triplanar(baseTex, v_tex);
- #else
- vec4 tex = texture2D(baseTex, v_tex.xy);
- #endif
+#if USE_TRIPLANAR
+ vec4 tex = triplanar(baseTex, v_tex);
+#else
+ vec4 tex = texture2D(baseTex, v_tex.xy);
+#endif
- #if USE_GRASS && LAYER
- if (tex.a < 0.05)
- discard;
- #endif
+#if USE_GRASS && LAYER
+ if (tex.a < 0.05)
+ discard;
+#endif
- #if DECAL
- // Use alpha from main texture
- gl_FragColor.a = tex.a;
- #endif
+#if DECAL
+ // Use alpha from main texture
+ gl_FragColor.a = tex.a;
+#endif
- vec3 texdiffuse = tex.rgb;
+ vec3 texdiffuse = tex.rgb;
- #if USE_SPECULAR || USE_SPECULAR_MAP || USE_NORMAL_MAP
- vec3 normal = v_normal;
- #endif
+#if USE_SPECULAR || USE_SPECULAR_MAP || USE_NORMAL_MAP
+ vec3 normal = v_normal;
+#endif
- #if USE_NORMAL_MAP
- float sign = v_tangent.w;
- mat3 tbn = mat3(v_tangent.xyz, v_bitangent * -sign, v_normal);
- #if USE_TRIPLANAR
- vec3 ntex = triplanarNormals(normTex, v_tex).rgb * 2.0 - 1.0;
- #else
- vec3 ntex = texture2D(normTex, v_tex).rgb * 2.0 - 1.0;
- #endif
- normal = normalize(tbn * ntex);
- vec3 bumplight = max(dot(-sunDir, normal), 0.0) * sunColor;
- vec3 sundiffuse = (bumplight - v_lighting.rgb) * effectSettings.x + v_lighting.rgb;
- #else
- vec3 sundiffuse = v_lighting;
- #endif
+#if USE_NORMAL_MAP
+ float sign = v_tangent.w;
+ mat3 tbn = mat3(v_tangent.xyz, v_bitangent * -sign, v_normal);
+#if USE_TRIPLANAR
+ vec3 ntex = triplanarNormals(normTex, v_tex).rgb * 2.0 - 1.0;
+#else
+ vec3 ntex = texture2D(normTex, v_tex).rgb * 2.0 - 1.0;
+#endif
+ normal = normalize(tbn * ntex);
+ vec3 bumplight = max(dot(-sunDir, normal), 0.0) * sunColor;
+ vec3 sundiffuse = (bumplight - v_lighting.rgb) * effectSettings.x + v_lighting.rgb;
+#else
+ vec3 sundiffuse = v_lighting;
+#endif
- vec4 specular = vec4(0.0);
- #if USE_SPECULAR || USE_SPECULAR_MAP
- vec3 specCol;
- float specPow;
- #if USE_SPECULAR_MAP
- #if USE_TRIPLANAR
- vec4 s = triplanar(specTex, v_tex);
- #else
- vec4 s = texture2D(specTex, v_tex);
- #endif
- specCol = s.rgb;
- specular.a = s.a;
- specPow = effectSettings.y;
- #else
- specCol = specularColor;
- specPow = specularPower;
- #endif
- specular.rgb = sunColor * specCol * pow(max(0.0, dot(normalize(normal), v_half)), specPow);
- #endif
+ vec4 specular = vec4(0.0);
+#if USE_SPECULAR || USE_SPECULAR_MAP
+ vec3 specCol;
+ float specPow;
+#if USE_SPECULAR_MAP
+#if USE_TRIPLANAR
+ vec4 s = triplanar(specTex, v_tex);
+#else
+ vec4 s = texture2D(specTex, v_tex);
+#endif
+ specCol = s.rgb;
+ specular.a = s.a;
+ specPow = effectSettings.y;
+#else
+ specCol = specularColor;
+ specPow = specularPower;
+#endif
+ specular.rgb = sunColor * specCol * pow(max(0.0, dot(normalize(normal), v_half)), specPow);
+#endif
- vec3 color = (texdiffuse * sundiffuse + specular.rgb) * get_shadow() + texdiffuse * ambient;
+ vec3 color = (texdiffuse * sundiffuse + specular.rgb) * get_shadow() + texdiffuse * ambient;
- #if USE_SPECULAR_MAP && USE_SELF_LIGHT
- color = mix(texdiffuse, color, specular.a);
- #endif
+#if USE_SPECULAR_MAP && USE_SELF_LIGHT
+ color = mix(texdiffuse, color, specular.a);
+#endif
- #if USE_FOG
- color = get_fog(color);
- #endif
+#if USE_FOG
+ color = get_fog(color);
+#endif
- float los = texture2D(losTex, v_los).a;
- los = los < 0.03 ? 0.0 : los;
- color *= los;
+ float los = texture2D(losTex, v_los).a;
+ los = los < 0.03 ? 0.0 : los;
+ color *= los;
- #if DECAL
- color *= shadingColor;
- #endif
+#if DECAL
+ color *= shadingColor;
+#endif
- gl_FragColor.rgb = color;
+ gl_FragColor.rgb = color;
- #if USE_GRASS
- gl_FragColor.a = tex.a;
- #endif
+#if USE_GRASS
+ gl_FragColor.a = tex.a;
+#endif
}
Index: binaries/data/mods/public/shaders/glsl/terrain_common.vs
===================================================================
--- binaries/data/mods/public/shaders/glsl/terrain_common.vs
+++ binaries/data/mods/public/shaders/glsl/terrain_common.vs
@@ -2,6 +2,7 @@
uniform mat4 transform;
uniform vec3 cameraPos;
+
#ifdef GL_ES
uniform mediump vec3 sunDir;
uniform mediump vec3 sunColor;
@@ -9,18 +10,19 @@
uniform vec3 sunDir;
uniform vec3 sunColor;
#endif
+
uniform vec2 textureTransform;
uniform vec2 losTransform;
uniform mat4 shadowTransform;
-#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
- uniform vec4 shadowScale;
+#if USE_SHADOW_SAMPLER && (USE_SHADOW_PCF || USE_SHADOW_PCSS)
+uniform vec4 shadowScale;
#endif
varying vec3 v_lighting;
#if USE_SHADOW
- varying vec4 v_shadow;
+varying vec4 v_shadow;
#endif
varying vec2 v_los;
@@ -27,22 +29,22 @@
varying vec2 v_blend;
#if USE_TRIPLANAR
- varying vec3 v_tex;
+varying vec3 v_tex;
#else
- varying vec2 v_tex;
+varying vec2 v_tex;
#endif
varying vec3 v_normal;
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP
- #if USE_NORMAL_MAP
- varying vec4 v_tangent;
- varying vec3 v_bitangent;
- #endif
- #if USE_SPECULAR || USE_SPECULAR_MAP
- varying vec3 v_half;
- #endif
+#if USE_NORMAL_MAP
+ varying vec4 v_tangent;
+ varying vec3 v_bitangent;
#endif
+#if USE_SPECULAR || USE_SPECULAR_MAP
+ varying vec3 v_half;
+#endif
+#endif
attribute vec3 a_vertex;
@@ -53,68 +55,67 @@
void main()
{
- vec4 position = vec4(a_vertex, 1.0);
+ vec4 position = vec4(a_vertex, 1.0);
- #if USE_GRASS && LAYER
- position.y = a_vertex.y + (a_normal.y * 0.015 * LAYER);
- #endif
+#if USE_GRASS && LAYER
+ position.y = a_vertex.y + (a_normal.y * 0.015 * LAYER);
+#endif
- gl_Position = transform * position;
+ gl_Position = transform * position;
- v_lighting = a_color * sunColor;
-
- #if DECAL
- v_tex.xy = a_uv0;
- #else
+ v_lighting = a_color * sunColor;
- #if USE_TRIPLANAR
- v_tex = a_vertex;
- #else
- // Compute texcoords from position and terrain-texture-dependent transform
- float c = textureTransform.x;
- float s = -textureTransform.y;
- v_tex = vec2(a_vertex.x * c + a_vertex.z * -s, a_vertex.x * -s + a_vertex.z * -c);
- #endif
+#if DECAL
+ v_tex.xy = a_uv0;
+#else
+#if USE_TRIPLANAR
+ v_tex = a_vertex;
+#else
+ // Compute texcoords from position and terrain-texture-dependent transform
+ float c = textureTransform.x;
+ float s = -textureTransform.y;
+ v_tex = vec2(a_vertex.x * c + a_vertex.z * -s, a_vertex.x * -s + a_vertex.z * -c);
+#endif
- #if GL_ES
- // XXX: Ugly hack to hide some precision issues in GLES
- #if USE_TRIPLANAR
- v_tex = mod(v_tex, vec3(9.0, 9.0, 9.0));
- #else
- v_tex = mod(v_tex, vec2(9.0, 9.0));
- #endif
- #endif
- #endif
+#if GL_ES
+// XXX: Ugly hack to hide some precision issues in GLES
+#if USE_TRIPLANAR
+ v_tex = mod(v_tex, vec3(9.0, 9.0, 9.0));
+#else
+ v_tex = mod(v_tex, vec2(9.0, 9.0));
+#endif
+#endif
+#endif
- #if BLEND
- v_blend = a_uv1;
- #endif
+#if BLEND
+ v_blend = a_uv1;
+#endif
- #if USE_SHADOW
- v_shadow = shadowTransform * vec4(a_vertex, 1.0);
- #if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
- v_shadow.xy *= shadowScale.xy;
- #endif
- #endif
-
- v_normal = a_normal;
+#if USE_SHADOW
+ v_shadow = shadowTransform * vec4(a_vertex, 1.0);
+#if USE_SHADOW_SAMPLER && (USE_SHADOW_PCF || USE_SHADOW_PCSS)
+ v_shadow.xy /= shadowScale.w;
+#endif
+#endif
- #if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_TRIPLANAR
- #if USE_NORMAL_MAP
- vec3 t = vec3(1.0, 0.0, 0.0);
- t = normalize(t - v_normal * dot(v_normal, t));
- v_tangent = vec4(t, -1.0);
- v_bitangent = cross(v_normal, t);
- #endif
+v_normal = a_normal;
- #if USE_SPECULAR || USE_SPECULAR_MAP
- vec3 eyeVec = cameraPos.xyz - position.xyz;
- #if USE_SPECULAR || USE_SPECULAR_MAP
- vec3 sunVec = -sunDir;
- v_half = normalize(sunVec + normalize(eyeVec));
- #endif
- #endif
- #endif
+#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_TRIPLANAR
+#if USE_NORMAL_MAP
+ vec3 t = vec3(1.0, 0.0, 0.0);
+ t = normalize(t - v_normal * dot(v_normal, t));
+ v_tangent = vec4(t, -1.0);
+ v_bitangent = cross(v_normal, t);
+#endif
- v_los = a_vertex.xz * losTransform.x + losTransform.yy;
+#if USE_SPECULAR || USE_SPECULAR_MAP
+ vec3 eyeVec = cameraPos.xyz - position.xyz;
+#if USE_SPECULAR || USE_SPECULAR_MAP
+ vec3 sunVec = -sunDir;
+ v_half = normalize(sunVec + normalize(eyeVec));
+#endif
+#endif
+#endif
+
+ v_los = a_vertex.xz * losTransform.x + losTransform.yy;
}
Index: source/graphics/MapReader.cpp
===================================================================
--- source/graphics/MapReader.cpp
+++ source/graphics/MapReader.cpp
@@ -1432,7 +1432,7 @@
}
if (pPostproc)
- pPostproc->SetPostEffect(L"default");
+ pPostproc->SetPostEffect(L"None");
std::wstring skySet;
GET_ENVIRONMENT_PROPERTY(envObj, SkySet, skySet)
Index: source/ps/CStrInternStatic.h
===================================================================
--- source/ps/CStrInternStatic.h
+++ source/ps/CStrInternStatic.h
@@ -31,6 +31,7 @@
X(2)
X(ALPHABLEND_PASS_BLEND)
X(ALPHABLEND_PASS_OPAQUE)
+X(BIG_BLUR)
X(BLEND)
X(BLOOM_NOP)
X(BLOOM_PASS_H)
@@ -50,8 +51,10 @@
X(SYS_HAS_GLSL)
X(SYS_PREFER_GLSL)
X(USE_FANCY_EFFECTS)
+X(USE_DOF)
X(USE_FP_SHADOW)
X(USE_GPU_SKINNING)
+X(USE_HDR)
X(USE_INSTANCING)
X(USE_NORMALS)
X(USE_OBJECTCOLOR)
@@ -60,6 +63,7 @@
X(USE_REFRACTION)
X(USE_SHADOW)
X(USE_SHADOW_PCF)
+X(USE_SHADOW_PCSS)
X(USE_SHADOW_SAMPLER)
X(USE_SHADOWS_ON_WATER)
X(USE_FOG)
@@ -77,7 +81,9 @@
X(ambient)
X(baseTex)
X(blendTex)
+X(blockerTex)
X(bloom)
+X(downscaleTex2)
X(blurTex2)
X(blurTex4)
X(blurTex8)
@@ -86,6 +92,7 @@
X(color)
X(colorAdd)
X(colorMul)
+X(contrast)
X(delta)
X(depthTex)
X(foamTex)
@@ -98,7 +105,6 @@
X(gui_solid)
X(gui_solid_mask)
X(gui_text)
-X(hdr)
X(height)
X(instancingTransform)
X(losMap)
Index: source/ps/GameSetup/Config.h
===================================================================
--- source/ps/GameSetup/Config.h
+++ source/ps/GameSetup/Config.h
@@ -65,6 +65,8 @@
// flag to switch on shadow PCF
extern bool g_ShadowPCF;
+// flag to switch on shadow PCSS
+extern bool g_ShadowPCSS;
// flag to switch on particles rendering
extern bool g_Particles;
// flag to switch on fog
@@ -78,6 +80,8 @@
extern bool g_PreferGLSL;
// Use screen-space postprocessing filters (HDR, bloom, DOF, etc)
extern bool g_PostProc;
+// Use depth of field
+extern bool g_DOF;
// Use smooth LOS interpolation
extern bool g_SmoothLOS;
Index: source/ps/GameSetup/Config.cpp
===================================================================
--- source/ps/GameSetup/Config.cpp
+++ source/ps/GameSetup/Config.cpp
@@ -41,6 +41,7 @@
bool g_Shadows = false;
bool g_ShadowPCF = false;
+bool g_ShadowPCSS = false;
bool g_WaterEffects = true;
bool g_WaterFancyEffects = false;
@@ -56,6 +57,7 @@
bool g_PreferGLSL = false;
bool g_PostProc = false;
+bool g_DOF = false;
bool g_SmoothLOS = false;
float g_Gamma = 1.0f;
@@ -96,6 +98,7 @@
CFG_GET_VAL("renderactors", g_RenderActors);
CFG_GET_VAL("shadows", g_Shadows);
CFG_GET_VAL("shadowpcf", g_ShadowPCF);
+ CFG_GET_VAL("shadowpcss", g_ShadowPCSS);
CFG_GET_VAL("watereffects", g_WaterEffects);
CFG_GET_VAL("waterfancyeffects", g_WaterFancyEffects);
@@ -111,6 +114,7 @@
CFG_GET_VAL("showsky", g_ShowSky);
CFG_GET_VAL("preferglsl", g_PreferGLSL);
CFG_GET_VAL("postproc", g_PostProc);
+ CFG_GET_VAL("dof", g_DOF);
CFG_GET_VAL("smoothlos", g_SmoothLOS);
CFG_GET_VAL("gui.scale", g_GuiScale);
}
Index: source/ps/GameSetup/GameSetup.cpp
===================================================================
--- source/ps/GameSetup/GameSetup.cpp
+++ source/ps/GameSetup/GameSetup.cpp
@@ -598,6 +598,8 @@
g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCF, g_ShadowPCF);
g_ConfigDB.SetValueBool(CFG_SYSTEM, "shadowpcf", g_ShadowPCF);
+ g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCSS, g_ShadowPCSS);
+ g_ConfigDB.SetValueBool(CFG_SYSTEM, "shadowpcss", g_ShadowPCSS);
g_Renderer.SetOptionBool(CRenderer::OPT_PARTICLES, g_Particles);
g_ConfigDB.SetValueBool(CFG_SYSTEM, "particles", g_Particles);
g_Renderer.SetOptionBool(CRenderer::OPT_FOG, g_Fog);
@@ -610,6 +612,8 @@
g_ConfigDB.SetValueBool(CFG_SYSTEM, "preferglsl", g_PreferGLSL);
g_Renderer.SetOptionBool(CRenderer::OPT_POSTPROC, g_PostProc);
g_ConfigDB.SetValueBool(CFG_SYSTEM, "postproc", g_PostProc);
+ g_Renderer.SetOptionBool(CRenderer::OPT_DOF, g_DOF);
+ g_ConfigDB.SetValueBool(CFG_SYSTEM, "dof", g_DOF);
g_Renderer.SetOptionBool(CRenderer::OPT_SMOOTHLOS, g_SmoothLOS);
g_ConfigDB.SetValueBool(CFG_SYSTEM, "smoothlos", g_SmoothLOS);
Index: source/renderer/PostprocManager.h
===================================================================
--- source/renderer/PostprocManager.h
+++ source/renderer/PostprocManager.h
@@ -40,6 +40,9 @@
// Indicates which of the ping-pong buffers is used for reading and which for drawing.
bool m_WhichBuffer;
+ // Indicates whether the current map has HDR enabled.
+ bool m_MapUsesHDR;
+
// The name and shader technique we are using. "default" name means no technique is used
// (i.e. while we do allocate the buffers, no effects are rendered).
CStrW m_PostProcEffect;
@@ -61,7 +64,7 @@
// GPU-based Gaussian blur in two passes. inOutTex contains the input image and will be filled
// with the blurred image. tempTex must have the same size as inOutTex.
// inWidth and inHeight are the dimensions of the images in texels.
- void ApplyBlurGauss(GLuint inOutTex, GLuint tempTex, int inWidth, int inHeight);
+ void ApplyBlurGauss(GLuint inOutTex, GLuint tempTex, int inWidth, int inHeight, bool bigBlur);
// Applies a pass of a given effect to the entire current framebuffer. The shader is
// provided with a number of general-purpose variables, including the rendered screen so far,
@@ -99,6 +102,9 @@
// Sets the current effect.
void SetPostEffect(const CStrW& name);
+ // Refreshes the shaders.
+ void MakeShadersDirty();
+
// Clears the two color buffers and depth buffer, and redirects all rendering
// to our textures instead of directly to the system framebuffer.
// @note CPostprocManager must be initialized first
Index: source/renderer/PostprocManager.cpp
===================================================================
--- source/renderer/PostprocManager.cpp
+++ source/renderer/PostprocManager.cpp
@@ -37,7 +37,7 @@
#if !CONFIG2_GLES
CPostprocManager::CPostprocManager()
- : m_IsInitialized(false), m_PingFbo(0), m_PongFbo(0), m_PostProcEffect(L"default"), m_ColorTex1(0), m_ColorTex2(0),
+ : m_IsInitialized(false), m_PingFbo(0), m_PongFbo(0), m_PostProcEffect(L"None"), m_ColorTex1(0), m_ColorTex2(0),
m_DepthTex(0), m_BloomFbo(0), m_BlurTex2a(0), m_BlurTex2b(0), m_BlurTex4a(0), m_BlurTex4b(0),
m_BlurTex8a(0), m_BlurTex8b(0), m_WhichBuffer(true)
{
@@ -102,15 +102,18 @@
{
Cleanup();
- #define GEN_BUFFER_RGBA(name, w, h) \
- glGenTextures(1, (GLuint*)&name); \
- glBindTexture(GL_TEXTURE_2D, name); \
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); \
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); \
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (!g_Renderer.m_Options.m_Postproc)
+ return; // Don't create textures if we're not going to use them.
+#define GEN_BUFFER_RGBA(name, w, h) \
+ glGenTextures(1, (GLuint*)&name); \
+ glBindTexture(GL_TEXTURE_2D, name); \
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); \
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); \
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
// Two fullscreen ping-pong textures.
GEN_BUFFER_RGBA(m_ColorTex1, m_Width, m_Height);
GEN_BUFFER_RGBA(m_ColorTex2, m_Width, m_Height);
@@ -128,7 +131,7 @@
GEN_BUFFER_RGBA(m_BlurTex8a, m_Width / 8, m_Height / 8);
GEN_BUFFER_RGBA(m_BlurTex8b, m_Width / 8, m_Height / 8);
- #undef GEN_BUFFER_RGBA
+#undef GEN_BUFFER_RGBA
// Allocate the Depth/Stencil texture.
glGenTextures(1, (GLuint*)&m_DepthTex);
@@ -135,7 +138,7 @@
glBindTexture(GL_TEXTURE_2D, m_DepthTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8_EXT, m_Width, m_Height,
- 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 0);
+ 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -151,10 +154,10 @@
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_2D, m_ColorTex1, 0);
+ GL_TEXTURE_2D, m_ColorTex1, 0);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
- GL_TEXTURE_2D, m_DepthTex, 0);
+ GL_TEXTURE_2D, m_DepthTex, 0);
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
@@ -166,10 +169,10 @@
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_2D, m_ColorTex2, 0);
+ GL_TEXTURE_2D, m_ColorTex2, 0);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
- GL_TEXTURE_2D, m_DepthTex, 0);
+ GL_TEXTURE_2D, m_DepthTex, 0);
status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
@@ -182,7 +185,7 @@
/*
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_2D, m_BloomTex1, 0);
+ GL_TEXTURE_2D, m_BloomTex1, 0);
status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
@@ -254,7 +257,7 @@
tech->EndPass();
}
-void CPostprocManager::ApplyBlurGauss(GLuint inOutTex, GLuint tempTex, int inWidth, int inHeight)
+void CPostprocManager::ApplyBlurGauss(GLuint inOutTex, GLuint tempTex, int inWidth, int inHeight, bool bigBlur)
{
// Set tempTex as our rendering target.
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_BloomFbo);
@@ -263,6 +266,8 @@
// Get bloom shader, for a horizontal Gaussian blur pass.
CShaderDefines defines2;
defines2.Add(str_BLOOM_PASS_H, str_1);
+ if (bigBlur)
+ defines2.Add(str_BIG_BLUR, str_1);
CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom,
g_Renderer.GetSystemShaderDefines(), defines2);
@@ -309,6 +314,8 @@
// Get bloom shader, for a vertical Gaussian blur pass.
CShaderDefines defines3;
defines3.Add(str_BLOOM_PASS_V, str_1);
+ if (bigBlur)
+ defines3.Add(str_BIG_BLUR, str_1);
tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom,
g_Renderer.GetSystemShaderDefines(), defines3);
@@ -340,19 +347,33 @@
int width = m_Width, height = m_Height;
- #define SCALE_AND_BLUR(tex1, tex2, temptex) \
+ #define SCALE_AND_BLUR_3_TAP(tex1, tex2, temptex) \
ApplyBlurDownscale2x(tex1, tex2, width, height); \
width /= 2; \
height /= 2; \
- ApplyBlurGauss(tex2, temptex, width, height);
+ ApplyBlurGauss(tex2, temptex, width, height, false); \
+ if (g_Renderer.m_Options.m_DOF) \
+ ApplyBlurDownscale2x(tex1, temptex, width * 2, height * 2);
- // We do the same thing for each scale, incrementally adding more and more blur.
- SCALE_AND_BLUR(m_WhichBuffer ? m_ColorTex1 : m_ColorTex2, m_BlurTex2a, m_BlurTex2b);
- SCALE_AND_BLUR(m_BlurTex2a, m_BlurTex4a, m_BlurTex4b);
- SCALE_AND_BLUR(m_BlurTex4a, m_BlurTex8a, m_BlurTex8b);
+ #define SCALE_AND_BLUR_5_TAP(tex1, tex2, temptex) \
+ ApplyBlurDownscale2x(tex1, tex2, width, height); \
+ width /= 2; \
+ height /= 2; \
+ ApplyBlurGauss(tex2, temptex, width, height, true);
- #undef SCALE_AND_BLUR
+ // Apply a small blur for dof and cache an extra non-blurred downscaled image for smooth interpolation.
+ SCALE_AND_BLUR_3_TAP(m_WhichBuffer ? m_ColorTex1 : m_ColorTex2, m_BlurTex2a, m_BlurTex2b);
+
+ // Apply a large blur for bloom.
+ if (m_MapUsesHDR && g_LightEnv.m_Bloom < 0.2f)
+ {
+ SCALE_AND_BLUR_5_TAP(m_BlurTex2a, m_BlurTex4a, m_BlurTex4b);
+ SCALE_AND_BLUR_5_TAP(m_BlurTex4a, m_BlurTex8a, m_BlurTex8b);
+ }
+ #undef SCALE_AND_BLUR_3_TAP
+ #undef SCALE_AND_BLUR_5_TAP
+
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, originalFBO);
}
@@ -424,10 +445,10 @@
shader->BindTexture(str_depthTex, m_DepthTex);
+ shader->BindTexture(str_downscaleTex2, m_BlurTex2b);
shader->BindTexture(str_blurTex2, m_BlurTex2a);
shader->BindTexture(str_blurTex4, m_BlurTex4a);
shader->BindTexture(str_blurTex8, m_BlurTex8a);
-
shader->Uniform(str_width, m_Width);
shader->Uniform(str_height, m_Height);
shader->Uniform(str_zNear, g_Game->GetView()->GetNear());
@@ -434,7 +455,7 @@
shader->Uniform(str_zFar, g_Game->GetView()->GetFar());
shader->Uniform(str_brightness, g_LightEnv.m_Brightness);
- shader->Uniform(str_hdr, g_LightEnv.m_Contrast);
+ shader->Uniform(str_contrast, g_LightEnv.m_Contrast);
shader->Uniform(str_saturation, g_LightEnv.m_Saturation);
shader->Uniform(str_bloom, g_LightEnv.m_Bloom);
@@ -475,10 +496,6 @@
{
ENSURE(m_IsInitialized);
- // Don't do anything if we are using the default effect.
- if (m_PostProcEffect == L"default")
- return;
-
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, 0, 0);
@@ -526,7 +543,7 @@
effects.push_back(path.Basename().string());
// Add the default "null" effect to the list.
- effects.push_back(L"default");
+ effects.push_back(L"None");
sort(effects.begin(), effects.end());
@@ -535,16 +552,34 @@
void CPostprocManager::SetPostEffect(const CStrW& name)
{
+ if (name == L"default" || name == L"None" || name == L"DOF")
+ {
+ m_MapUsesHDR = false;
+ m_PostProcEffect = L"None";
+ }else if (name == L"hdr" || name == L"HDR")
+ {
+ m_MapUsesHDR = true;
+ m_PostProcEffect = L"HDR";
+ }else
+ {
+ // Custom effects are not actually supported without modifying PostprocManager. This is a stub!
+ //CStrW n = L"postproc/" + name;
+ }
+ MakeShadersDirty();
+}
+
+void CPostprocManager::MakeShadersDirty()
+{
if (m_IsInitialized)
{
- if (name != L"default")
- {
- CStrW n = L"postproc/" + name;
- m_PostProcTech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern(n.ToUTF8()));
- }
+ CStrW n = L"postproc/HDR";
+ CShaderDefines techdefines;
+ if (g_Renderer.m_Options.m_DOF)
+ techdefines.Add(str_USE_DOF, str_1);
+ if (m_MapUsesHDR)
+ techdefines.Add(str_USE_HDR, str_1);
+ m_PostProcTech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern(n.ToUTF8()), g_Renderer.GetSystemShaderDefines(), techdefines);
}
-
- m_PostProcEffect = name;
}
#else
Index: source/renderer/RenderModifiers.cpp
===================================================================
--- source/renderer/RenderModifiers.cpp
+++ source/renderer/RenderModifiers.cpp
@@ -79,10 +79,10 @@
if (GetShadowMap() && shader->GetTextureBinding(str_shadowTex).Active())
{
shader->BindTexture(str_shadowTex, GetShadowMap()->GetTexture());
+ shader->BindTexture(str_blockerTex, GetShadowMap()->GetBlockerTexture());
shader->Uniform(str_shadowTransform, GetShadowMap()->GetTextureMatrix());
- int width = GetShadowMap()->GetWidth();
- int height = GetShadowMap()->GetHeight();
- shader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height);
+ const CVector4D& shadowscale = GetShadowMap()->GetShadowScale();
+ shader->Uniform(str_shadowScale, shadowscale.X, shadowscale.Y, shadowscale.Z, shadowscale.W);
}
if (GetLightEnv())
Index: source/renderer/Renderer.h
===================================================================
--- source/renderer/Renderer.h
+++ source/renderer/Renderer.h
@@ -83,6 +83,7 @@
OPT_WATERREFRACTION,
OPT_SHADOWSONWATER,
OPT_SHADOWPCF,
+ OPT_SHADOWPCSS,
OPT_PARTICLES,
OPT_PREFERGLSL,
OPT_FOG,
@@ -90,6 +91,7 @@
OPT_SHOWSKY,
OPT_SMOOTHLOS,
OPT_POSTPROC,
+ OPT_DOF,
OPT_DISPLAYFRUSTUM,
};
@@ -151,6 +153,7 @@
bool m_ShadowAlphaFix;
bool m_ARBProgramShadow;
bool m_ShadowPCF;
+ bool m_ShadowPCSS;
bool m_Particles;
bool m_PreferGLSL;
bool m_ForceAlphaTest;
@@ -160,6 +163,7 @@
bool m_SmoothLOS;
bool m_ShowSky;
bool m_Postproc;
+ bool m_DOF;
bool m_DisplayFrustum;
} m_Options;
Index: source/renderer/Renderer.cpp
===================================================================
--- source/renderer/Renderer.cpp
+++ source/renderer/Renderer.cpp
@@ -440,6 +440,7 @@
m_Options.m_ShadowAlphaFix = true;
m_Options.m_ARBProgramShadow = true;
m_Options.m_ShadowPCF = false;
+ m_Options.m_ShadowPCSS = false;
m_Options.m_Particles = false;
m_Options.m_Silhouettes = false;
m_Options.m_PreferGLSL = false;
@@ -573,8 +574,10 @@
m->globalContext.Add(str_USE_SHADOW, str_1);
if (m_Caps.m_ARBProgramShadow && m_Options.m_ARBProgramShadow)
m->globalContext.Add(str_USE_FP_SHADOW, str_1);
- if (m_Options.m_ShadowPCF)
+ if (m_Options.m_ShadowPCF && !m_Options.m_ShadowPCSS)
m->globalContext.Add(str_USE_SHADOW_PCF, str_1);
+ if (m_Options.m_ShadowPCF && m_Options.m_ShadowPCSS)
+ m->globalContext.Add(str_USE_SHADOW_PCSS, str_1);
#if !CONFIG2_GLES
m->globalContext.Add(str_USE_SHADOW_SAMPLER, str_1);
#endif
@@ -663,6 +666,8 @@
if (m_Options.m_Postproc)
m->postprocManager.Initialize();
+ m->shadow.RecreateTexture();
+
return true;
}
@@ -669,12 +674,12 @@
// resize renderer view
void CRenderer::Resize(int width, int height)
{
+ m_Width = width;
+ m_Height = height;
+
// need to recreate the shadow map object to resize the shadow texture
m->shadow.RecreateTexture();
- m_Width = width;
- m_Height = height;
-
m->postprocManager.Resize();
m_WaterManager->Resize();
@@ -717,7 +722,14 @@
case OPT_SHADOWPCF:
m_Options.m_ShadowPCF = value;
MakeShadersDirty();
+ if (m_Options.m_ShadowPCSS)
+ m->shadow.RecreateTexture();
break;
+ case OPT_SHADOWPCSS:
+ m_Options.m_ShadowPCSS = value;
+ MakeShadersDirty();
+ m->shadow.RecreateTexture();
+ break;
case OPT_PARTICLES:
m_Options.m_Particles = value;
break;
@@ -741,7 +753,12 @@
break;
case OPT_POSTPROC:
m_Options.m_Postproc = value;
+ m->postprocManager.Resize();
break;
+ case OPT_DOF:
+ m_Options.m_DOF = value;
+ m->postprocManager.MakeShadersDirty();
+ break;
case OPT_DISPLAYFRUSTUM:
m_Options.m_DisplayFrustum = value;
break;
@@ -774,6 +791,8 @@
return m_Options.m_WaterShadows;
case OPT_SHADOWPCF:
return m_Options.m_ShadowPCF;
+ case OPT_SHADOWPCSS:
+ return m_Options.m_ShadowPCSS;
case OPT_PARTICLES:
return m_Options.m_Particles;
case OPT_PREFERGLSL:
@@ -788,6 +807,8 @@
return m_Options.m_SmoothLOS;
case OPT_POSTPROC:
return m_Options.m_Postproc;
+ case OPT_DOF:
+ return m_Options.m_DOF;
case OPT_DISPLAYFRUSTUM:
return m_Options.m_DisplayFrustum;
default:
@@ -1237,10 +1258,10 @@
else
{
// Render terrain and models
+ RenderModels(context, CULL_REFLECTIONS);
+ ogl_WarnIfError();
RenderPatches(context, CULL_REFLECTIONS);
ogl_WarnIfError();
- RenderModels(context, CULL_REFLECTIONS);
- ogl_WarnIfError();
RenderTransparentModels(context, CULL_REFLECTIONS, TRANSPARENT, true);
ogl_WarnIfError();
}
@@ -1311,10 +1332,10 @@
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render terrain and models
+ RenderModels(context, CULL_REFRACTIONS);
+ ogl_WarnIfError();
RenderPatches(context, CULL_REFRACTIONS);
ogl_WarnIfError();
- RenderModels(context, CULL_REFRACTIONS);
- ogl_WarnIfError();
RenderTransparentModels(context, CULL_REFRACTIONS, TRANSPARENT_OPAQUE, false);
ogl_WarnIfError();
@@ -1531,56 +1552,50 @@
}
}
+ // Render submitted terrain patches and models
+ RenderModels(context, cullGroup);
+ ogl_WarnIfError();
+
+ RenderPatches(context, cullGroup);
+ ogl_WarnIfError();
+
if (m_Options.m_ShowSky)
{
m->skyManager.RenderSky();
}
- // render submitted patches and models
- RenderPatches(context, cullGroup);
+ // Render the occluding parts of alpha-blended objects.
+ RenderTransparentModels(context, cullGroup, TRANSPARENT_OPAQUE, false);
ogl_WarnIfError();
- // render debug-related terrain overlays
+ // Render debug-related terrain overlays
ITerrainOverlay::RenderOverlaysBeforeWater();
ogl_WarnIfError();
- // render other debug-related overlays before water (so they can be seen when underwater)
+ // Render other debug-related overlays before water (so they can be seen when underwater)
m->overlayRenderer.RenderOverlaysBeforeWater();
ogl_WarnIfError();
- RenderModels(context, cullGroup);
- ogl_WarnIfError();
-
// render water
if (m_WaterManager->m_RenderWater && g_Game && waterScissor.GetVolume() > 0)
{
- // render transparent stuff, but only the solid parts that can occlude block water
- RenderTransparentModels(context, cullGroup, TRANSPARENT_OPAQUE, false);
- ogl_WarnIfError();
-
m->terrainRenderer.RenderWater(context, cullGroup, &m->shadow);
ogl_WarnIfError();
-
- // render transparent stuff again, but only the blended parts that overlap water
- RenderTransparentModels(context, cullGroup, TRANSPARENT_BLEND, false);
- ogl_WarnIfError();
}
- else
- {
- // render transparent stuff, so it can overlap models/terrain
- RenderTransparentModels(context, cullGroup, TRANSPARENT, false);
- ogl_WarnIfError();
- }
- // render debug-related terrain overlays
+ // Render the transparent parts of alpha-blended objects.
+ RenderTransparentModels(context, cullGroup, TRANSPARENT_BLEND, false);
+ ogl_WarnIfError();
+
+ // Render debug-related terrain overlays
ITerrainOverlay::RenderOverlaysAfterWater(cullGroup);
ogl_WarnIfError();
- // render some other overlays after water (so they can be displayed on top of water)
+ // Render some other overlays after water (so they can be displayed on top of water)
m->overlayRenderer.RenderOverlaysAfterWater();
ogl_WarnIfError();
- // particles are transparent so render after water
+ // Particles are transparent so render after water
if (m_Options.m_Particles)
{
RenderParticles(cullGroup);
Index: source/renderer/ShadowMap.h
===================================================================
--- source/renderer/ShadowMap.h
+++ source/renderer/ShadowMap.h
@@ -136,6 +136,14 @@
GLuint GetTexture() const;
/**
+ * GetBlockerTexture: Retrieve the OpenGL texture object name that contains the shadow map copy used
+ * for blocker search by PCSS.
+ *
+ * @return the texture name of the shadow map blocker search texture
+ */
+ GLuint GetBlockerTexture() const;
+
+ /**
* GetTextureMatrix: Retrieve the world-space to shadow map texture coordinates
* transformation matrix.
*
@@ -145,6 +153,14 @@
const CMatrix3D& GetTextureMatrix() const;
/**
+ * GetLightspaceScale: Retrieve the 3D scale factor for scaling shadow filter kernels.
+ *
+ * @return the vector representing the relative scale of the shadow map resolution
+ * and the bounds of the light space frustum.
+ */
+ const CVector4D& GetShadowScale() const;
+
+ /**
* Visualize shadow mapping calculations to help in
* debugging and optimal shadow map usage.
*/
Index: source/renderer/ShadowMap.cpp
===================================================================
--- source/renderer/ShadowMap.cpp
+++ source/renderer/ShadowMap.cpp
@@ -52,8 +52,10 @@
int DepthTextureBits;
// the EXT_framebuffer_object framebuffer
GLuint Framebuffer;
+ GLuint BlockerFramebuffer;
// handle of shadow map
GLuint Texture;
+ GLuint blockerTexture;
// width, height of shadow map
int Width, Height;
// Shadow map quality (-2 - Very Low, -1 - Low, 0 - Medium, 1 - High, 2 - Very High)
@@ -78,6 +80,8 @@
CBoundingBoxAligned ShadowRenderBound;
+ CVector4D ShadowScale;
+
// Camera transformed into light space
CCamera LightspaceCamera;
@@ -96,7 +100,7 @@
// Helper functions
void CalcShadowMatrices();
- void CreateTexture();
+ int CreateTexture();
};
@@ -106,7 +110,9 @@
{
m = new ShadowMapInternals;
m->Framebuffer = 0;
+ m->BlockerFramebuffer = 0;
m->Texture = 0;
+ m->blockerTexture = 0;
m->DummyTexture = 0;
m->Width = 0;
m->Height = 0;
@@ -129,10 +135,14 @@
{
if (m->Texture)
glDeleteTextures(1, &m->Texture);
+ if (m->blockerTexture)
+ glDeleteTextures(1, &m->blockerTexture);
if (m->DummyTexture)
glDeleteTextures(1, &m->DummyTexture);
if (m->Framebuffer)
pglDeleteFramebuffersEXT(1, &m->Framebuffer);
+ if (m->BlockerFramebuffer)
+ pglDeleteFramebuffersEXT(1, &m->BlockerFramebuffer);
delete m;
}
@@ -142,18 +152,32 @@
// size has changed
void ShadowMap::RecreateTexture()
{
- if (m->Texture)
- glDeleteTextures(1, &m->Texture);
- if (m->DummyTexture)
- glDeleteTextures(1, &m->DummyTexture);
- if (m->Framebuffer)
- pglDeleteFramebuffersEXT(1, &m->Framebuffer);
+ bool good = false;
+ while (!good)
+ {
+ if (m->Texture)
+ glDeleteTextures(1, &m->Texture);
+ if (m->blockerTexture)
+ glDeleteTextures(1, &m->blockerTexture);
+ if (m->DummyTexture)
+ glDeleteTextures(1, &m->DummyTexture);
+ if (m->Framebuffer)
+ pglDeleteFramebuffersEXT(1, &m->Framebuffer);
+ if (m->BlockerFramebuffer)
+ pglDeleteFramebuffersEXT(1, &m->BlockerFramebuffer);
- m->Texture = 0;
- m->DummyTexture = 0;
- m->Framebuffer = 0;
+ m->Texture = 0;
+ m->blockerTexture = 0;
+ m->DummyTexture = 0;
+ m->Framebuffer = 0;
+ m->BlockerFramebuffer = 0;
- // (Texture will be constructed in next SetupFrame)
+ good = true;
+ if (m->CreateTexture() == -1)
+ {
+ good = false;
+ }
+ }
}
//////////////////////////////////////////////////////////////////////////////
@@ -160,9 +184,6 @@
// SetupFrame: camera and light direction for this frame
void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
{
- if (!m->Texture)
- m->CreateTexture();
-
CVector3D z = lightdir;
CVector3D y;
CVector3D x = camera.m_Orientation.GetIn();
@@ -306,6 +327,7 @@
ShadowRenderBound[1].X = ceil(ShadowRenderBound[1].X / boundInc) * boundInc;
ShadowRenderBound[1].Y = ceil(ShadowRenderBound[1].Y / boundInc) * boundInc;
+
// Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
CVector3D scale = ShadowRenderBound[1] - ShadowRenderBound[0];
CVector3D shift = (ShadowRenderBound[1] + ShadowRenderBound[0]) * -0.5;
@@ -324,6 +346,7 @@
// make sure a given world position falls on a consistent shadowmap texel fractional offset
float offsetX = fmod(ShadowRenderBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth));
float offsetY = fmod(ShadowRenderBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight));
+
LightProjection.SetZero();
LightProjection._11 = scale.X;
@@ -351,6 +374,12 @@
lightToTex._34 = -ShadowRenderBound[0].Z * texscalez;
lightToTex._44 = 1.0;
+ // Set up the shadow scaling factors for constant-width PCF/PCSS filtering.
+ ShadowScale.X = (float)Width / (2048.f * (ShadowRenderBound[1].X - ShadowRenderBound[0].X)/100.f);
+ ShadowScale.Y = (float)Width / (2048.f * (ShadowRenderBound[1].Y - ShadowRenderBound[0].Y)/100.f);
+ ShadowScale.Z = (ShadowRenderBound[1].Z - ShadowRenderBound[0].Z) / 100.f;
+ ShadowScale.W = 1.f / (float)Width;
+
TextureMatrix = lightToTex * LightTransform;
}
@@ -358,25 +387,9 @@
//////////////////////////////////////////////////////////////////////////
// Create the shadow map
-void ShadowMapInternals::CreateTexture()
+int ShadowMapInternals::CreateTexture()
{
- // Cleanup
- if (Texture)
- {
- glDeleteTextures(1, &Texture);
- Texture = 0;
- }
- if (DummyTexture)
- {
- glDeleteTextures(1, &DummyTexture);
- DummyTexture = 0;
- }
- if (Framebuffer)
- {
- pglDeleteFramebuffersEXT(1, &Framebuffer);
- Framebuffer = 0;
- }
-
+ int ret = 0;
// save the caller's FBO
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &SavedViewFBO);
@@ -395,20 +408,16 @@
int shadow_map_size = (int)round_up_to_pow2((unsigned)std::max(g_Renderer.GetWidth(), g_Renderer.GetHeight()));
switch (QualityLevel)
{
- // Very Low
- case -2:
- shadow_map_size /= 4;
- break;
// Low
- case -1:
+ case 0:
shadow_map_size /= 2;
break;
// High
- case 1:
+ case 2:
shadow_map_size *= 2;
break;
// Ultra
- case 2:
+ case 3:
shadow_map_size *= 4;
break;
// Medium as is
@@ -426,19 +435,24 @@
EffectiveHeight = Height;
const char* formatname;
+ GLenum format;
- switch(DepthTextureBits)
- {
- case 16: formatname = "DEPTH_COMPONENT16"; break;
- case 24: formatname = "DEPTH_COMPONENT24"; break;
- case 32: formatname = "DEPTH_COMPONENT32"; break;
- default: formatname = "DEPTH_COMPONENT"; break;
- }
+ #if CONFIG2_GLES
+ formatname = "DEPTH_COMPONENT";
+ format = GL_DEPTH_COMPONENT;
+ #else
+ switch (DepthTextureBits)
+ {
+ case 16: formatname = "DEPTH_COMPONENT16"; format = GL_DEPTH_COMPONENT16; break;
+ case 24: formatname = "DEPTH_COMPONENT24"; format = GL_DEPTH_COMPONENT24; break;
+ case 32: formatname = "DEPTH_COMPONENT32"; format = GL_DEPTH_COMPONENT32; break;
+ default: formatname = "DEPTH_COMPONENT16"; format = GL_DEPTH_COMPONENT16; break;
+ }
+ #endif
- LOGMESSAGE("Creating shadow texture (size %dx%d) (format = %s)",
+ LOGMESSAGE("Creating shadow textures (size %d x %d) (format = %s)",
Width, Height, formatname);
-
if (g_Renderer.m_Options.m_ShadowAlphaFix)
{
glGenTextures(1, &DummyTexture);
@@ -453,20 +467,8 @@
glGenTextures(1, &Texture);
g_Renderer.BindTexture(0, Texture);
- GLenum format;
+
-#if CONFIG2_GLES
- format = GL_DEPTH_COMPONENT;
-#else
- switch (DepthTextureBits)
- {
- case 16: format = GL_DEPTH_COMPONENT16; break;
- case 24: format = GL_DEPTH_COMPONENT24; break;
- case 32: format = GL_DEPTH_COMPONENT32; break;
- default: format = GL_DEPTH_COMPONENT; break;
- }
-#endif
-
glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
// GLES requires type == UNSIGNED_SHORT or UNSIGNED_INT
@@ -485,7 +487,7 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
- // Use GL_LINEAR to trigger automatic PCF on some devices
+ // Use GL_LINEAR for higher quality sampling.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#endif
@@ -502,32 +504,82 @@
{
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, DummyTexture, 0);
}
- else
+
+ ogl_WarnIfError();
+
+ GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ ret = -1;
+
+ if (g_Renderer.m_Options.m_ShadowPCF && g_Renderer.m_Options.m_ShadowPCSS)
{
+ // generate texture for PCSS blocker search
+ pglGenFramebuffersEXT(1, &BlockerFramebuffer);
+ glGenTextures(1, &blockerTexture);
+ g_Renderer.BindTexture(0, blockerTexture);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
+ // GLES requires type == UNSIGNED_SHORT or UNSIGNED_INT
+
+ // set texture parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
#if CONFIG2_GLES
-#warning TODO: figure out whether the glDrawBuffer/glReadBuffer stuff is needed, since it is not supported by GLES
+ // GLES doesn't do depth comparisons, so treat it as a
+ // basic unfiltered depth texture
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#else
- glDrawBuffer(GL_NONE);
-#endif
- }
+ // Disable automatic depth comparisons
+ glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
-#if !CONFIG2_GLES
- glReadBuffer(GL_NONE);
+ // Use GL_NEAREST for accurate blocker search.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#endif
- ogl_WarnIfError();
+ ogl_WarnIfError();
- GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ // bind to framebuffer object
+ glBindTexture(GL_TEXTURE_2D, 0);
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, BlockerFramebuffer);
- pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, SavedViewFBO);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, blockerTexture, 0);
- if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ ogl_WarnIfError();
+
+ status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ ret = -1;
+ }
+ }
+
+ if (ret != 0)
{
- LOGWARNING("Framebuffer object incomplete: 0x%04X", status);
+ if (QualityLevel > 0)
+ {
+ LOGWARNING("Failed to allocate shadow map! Reducing shadow map quality!");
+ g_ConfigDB.SetValueString(CFG_USER, "shadowquality", std::to_string(--QualityLevel));
+ }
+ else
+ {
+ ret = -2;
+ LOGWARNING("Failed to allocate shadow map! Disabling shadows!");
- // Disable shadow rendering (but let the user try again if they want)
- g_Renderer.m_Options.m_Shadows = false;
+ // Disable shadow rendering (but let the user try again if they want)
+ g_Renderer.m_Options.m_Shadows = false;
+ g_Renderer.MakeShadersDirty();
+ g_ConfigDB.SetValueBool(CFG_USER, "shadows", false);
+ }
}
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, SavedViewFBO);
+ return ret;
}
@@ -586,6 +638,14 @@
// Finish rendering into shadow map texture
void ShadowMap::EndRender()
{
+ if (g_Renderer.m_Options.m_ShadowPCF && g_Renderer.m_Options.m_ShadowPCSS && m->BlockerFramebuffer)
+ {
+ // Copy the shadow map for PCSS blocker search
+ pglBindFramebufferEXT(GL_READ_FRAMEBUFFER, m->Framebuffer);
+ pglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, m->BlockerFramebuffer);
+ pglBlitFramebufferEXT(0, 0, m->EffectiveWidth, m->EffectiveHeight, 0, 0, m->EffectiveWidth, m->EffectiveHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ }
+
glDisable(GL_SCISSOR_TEST);
g_Renderer.SetViewCamera(m->SavedViewCamera);
@@ -609,12 +669,22 @@
return m->Texture;
}
+GLuint ShadowMap::GetBlockerTexture() const
+{
+ return m->blockerTexture;
+}
+
const CMatrix3D& ShadowMap::GetTextureMatrix() const
{
return m->TextureMatrix;
}
+const CVector4D& ShadowMap::GetShadowScale() const
+{
+ return m->ShadowScale;
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
// Depth texture bits
int ShadowMap::GetDepthTextureBits() const
@@ -626,14 +696,8 @@
{
if (bits != m->DepthTextureBits)
{
- if (m->Texture)
- {
- glDeleteTextures(1, &m->Texture);
- m->Texture = 0;
- }
- m->Width = m->Height = 0;
-
m->DepthTextureBits = bits;
+ RecreateTexture();
}
}
Index: source/renderer/TerrainRenderer.cpp
===================================================================
--- source/renderer/TerrainRenderer.cpp
+++ source/renderer/TerrainRenderer.cpp
@@ -458,10 +458,10 @@
if (shadow)
{
shader->BindTexture(str_shadowTex, shadow->GetTexture());
+ shader->BindTexture(str_blockerTex, shadow->GetBlockerTexture());
shader->Uniform(str_shadowTransform, shadow->GetTextureMatrix());
- int width = shadow->GetWidth();
- int height = shadow->GetHeight();
- shader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height);
+ const CVector4D& shadowscale = shadow->GetShadowScale();
+ shader->Uniform(str_shadowScale, shadowscale.X, shadowscale.Y, shadowscale.Z, shadowscale.W);
}
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
Index: source/renderer/scripting/JSInterface_Renderer.h
===================================================================
--- source/renderer/scripting/JSInterface_Renderer.h
+++ source/renderer/scripting/JSInterface_Renderer.h
@@ -33,6 +33,7 @@
DECLARE_BOOLEAN_SCRIPT_SETTING(Shadows);
DECLARE_BOOLEAN_SCRIPT_SETTING(ShadowPCF);
+ DECLARE_BOOLEAN_SCRIPT_SETTING(ShadowPCSS);
DECLARE_BOOLEAN_SCRIPT_SETTING(Particles);
DECLARE_BOOLEAN_SCRIPT_SETTING(PreferGLSL);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterEffects);
@@ -46,6 +47,7 @@
DECLARE_BOOLEAN_SCRIPT_SETTING(ShowSky);
DECLARE_BOOLEAN_SCRIPT_SETTING(SmoothLOS);
DECLARE_BOOLEAN_SCRIPT_SETTING(Postproc);
+ DECLARE_BOOLEAN_SCRIPT_SETTING(DOF);
DECLARE_BOOLEAN_SCRIPT_SETTING(DisplayFrustum);
void RegisterScriptFunctions(const ScriptInterface& scriptInterface);
Index: source/renderer/scripting/JSInterface_Renderer.cpp
===================================================================
--- source/renderer/scripting/JSInterface_Renderer.cpp
+++ source/renderer/scripting/JSInterface_Renderer.cpp
@@ -38,6 +38,7 @@
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATEREFFECTS, WaterEffects);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERFANCYEFFECTS, WaterFancyEffects);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHADOWPCF, ShadowPCF);
+IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHADOWPCSS, ShadowPCSS);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHADOWS, Shadows);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERREALDEPTH, WaterRealDepth);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERREFLECTION, WaterReflection);
@@ -48,6 +49,7 @@
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHOWSKY, ShowSky);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SMOOTHLOS, SmoothLOS);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(POSTPROC, Postproc);
+IMPLEMENT_BOOLEAN_SCRIPT_SETTING(DOF, DOF);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(DISPLAYFRUSTUM, DisplayFrustum);
#undef IMPLEMENT_BOOLEAN_SCRIPT_SETTING
@@ -79,6 +81,7 @@
scriptInterface.RegisterFunction("Renderer_RecreateShadowMap");
REGISTER_BOOLEAN_SCRIPT_SETTING(Shadows);
REGISTER_BOOLEAN_SCRIPT_SETTING(ShadowPCF);
+ REGISTER_BOOLEAN_SCRIPT_SETTING(ShadowPCSS);
REGISTER_BOOLEAN_SCRIPT_SETTING(Particles);
REGISTER_BOOLEAN_SCRIPT_SETTING(PreferGLSL);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterEffects);
@@ -92,6 +95,7 @@
REGISTER_BOOLEAN_SCRIPT_SETTING(ShowSky);
REGISTER_BOOLEAN_SCRIPT_SETTING(SmoothLOS);
REGISTER_BOOLEAN_SCRIPT_SETTING(Postproc);
+ REGISTER_BOOLEAN_SCRIPT_SETTING(DOF);
REGISTER_BOOLEAN_SCRIPT_SETTING(DisplayFrustum);
}