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); }