#version 120 //#extension GL_EXT_gpu_shader4: enable #define DISABLE_STENCIL_TEXTURE //when = 1: the samples are filtered so that "halos" do not appear (new) // = 0: the samples are weighted based on heuristic (old) #define ENABLE_ANTI_HALO_PATCH 1 varying vec3 var_Position; varying vec3 var_WorldLightDir; varying vec3 var_tc0; varying vec3 var_tc6; varying vec2 var_TexDiffuse; varying vec2 var_TexNormal; varying vec2 var_TexSpecular; varying vec4 var_TexLight; varying mat3 var_TangentBitangentNormalMatrix; varying vec4 var_Color; //varying vec4 var_ScreenPos; uniform sampler2D u_normalTexture; uniform sampler2D u_lightFalloffTexture; uniform sampler2D u_lightProjectionTexture; uniform samplerCube u_lightProjectionCubemap; uniform sampler2D u_diffuseTexture; uniform sampler2D u_specularTexture; uniform sampler2D u_depthTexture; #ifndef DISABLE_STENCIL_TEXTURE uniform usampler2D u_stencilTexture; #endif //uniform samplerCubeShadow u_shadowMap; uniform samplerCube u_shadowMap; uniform vec4 u_lightOrigin; uniform vec3 u_lightOrigin2; uniform vec4 u_viewOrigin; uniform vec4 u_diffuseColor; uniform vec4 u_specularColor; uniform float u_shadows; uniform float u_advanced; uniform float u_cubic; uniform int u_softShadowsQuality; uniform float u_softShadowsRadius; uniform int u_shadowMipMap; uniform vec2 u_renderResolution; uniform mat4 u_modelMatrix; uniform float u_RGTC; // common local variables vec3 lightDir = u_lightOrigin.xyz - var_Position; vec3 viewDir = u_viewOrigin.xyz - var_Position; // compute normalized light, view and half angle vectors vec3 L = normalize( lightDir ); vec3 V = normalize( viewDir ); vec3 H = normalize( L + V ); // compute normal from normal map, move from [0, 1] to [-1, 1] range, normalize vec4 bumpTexel = texture2D ( u_normalTexture, var_TexNormal.st ) * 2. - 1.; vec3 RawN = u_RGTC == 1. ? vec3(bumpTexel.x, bumpTexel.y, sqrt(max(1.-bumpTexel.x*bumpTexel.x-bumpTexel.y*bumpTexel.y, 0))) : normalize( bumpTexel.wyz ); vec3 N = var_TangentBitangentNormalMatrix * RawN; float NdotH = clamp( dot( N, H ), 0.0, 1.0 ); float NdotL = clamp( dot( N, L ), 0.0, 1.0 ); float NdotV = clamp( dot( N, V ), 0.0, 1.0 ); vec3 lightColor() { // compute light projection and falloff vec3 lightColor; if (u_cubic == 1.0) { vec3 cubeTC = var_TexLight.xyz * 2.0 - 1.0; lightColor = textureCube(u_lightProjectionCubemap, cubeTC).rgb; float att = clamp(1.0-length(cubeTC), 0.0, 1.0); lightColor *= att*att; } else { vec3 lightProjection = texture2DProj( u_lightProjectionTexture, var_TexLight.xyw ).rgb; vec3 lightFalloff = texture2D( u_lightFalloffTexture, vec2( var_TexLight.z, 0.5 ) ).rgb; lightColor = lightProjection * lightFalloff; } return lightColor; } vec3 simpleInteraction() { // compute the diffuse term vec3 diffuse = texture2D( u_diffuseTexture, var_TexDiffuse ).rgb * u_diffuseColor.rgb; // compute the specular term float specularPower = 10.0; float specularContribution = pow( NdotH, specularPower ); vec3 specular = texture2D( u_specularTexture, var_TexSpecular ).rgb * specularContribution * u_specularColor.rgb; // compute lighting model vec3 finalColor = ( diffuse + specular ) * NdotL * lightColor() * var_Color.rgb; return finalColor; } vec3 advancedInteraction() { vec4 fresnelParms = vec4( 1.0, .23, .5, 1.0 ); vec4 fresnelParms2 = vec4( .2, .023, 120.0, 4.0 ); vec4 lightParms = vec4( .7, 1.8, 10.0, 30.0 ); vec3 diffuse = texture2D( u_diffuseTexture, var_TexDiffuse ).rgb; vec3 specular = vec3(0.026); //default value if texture not set?... if (dot(u_specularColor, u_specularColor) > 0.0) specular = texture2D( u_specularTexture, var_TexSpecular ).rgb; vec3 localL = normalize(var_tc0); vec3 localV = normalize(var_tc6); //must be done in tangent space, otherwise smoothing will suffer (see #4958) float NdotL = clamp(dot(RawN, localL), 0.0, 1.0); float NdotV = clamp(dot(RawN, localV), 0.0, 1.0); float NdotH = clamp(dot(RawN, normalize(localV + localL)), 0.0, 1.0); // fresnel part, ported from test_direct.vfp float fresnelTerm = pow(1.0 - NdotV, fresnelParms2.w); float rimLight = fresnelTerm * clamp(NdotL - 0.3, 0.0, fresnelParms.z) * lightParms.y; float specularPower = mix(lightParms.z, lightParms.w, specular.z); float specularCoeff = pow(NdotH, specularPower) * fresnelParms2.z; float fresnelCoeff = fresnelTerm * fresnelParms.y + fresnelParms2.y; vec3 specularColor = specularCoeff * fresnelCoeff * specular * (diffuse * 0.25 + vec3(0.75)); float R2f = clamp(localL.z * 4.0, 0.0, 1.0); float light = rimLight * R2f + NdotL; vec3 totalColor = (specularColor * R2f + diffuse) * light * u_diffuseColor.rgb * lightColor() * var_Color.rgb; return totalColor; } uniform vec2 u_softShadowsSamples[150]; //TODO: what cap is appropriate here? //returns eye Z coordinate with reversed sign (monotonically increasing with depth) float depthToZ(float depth) { float clipZ = 2.0 * depth - 1.0; float A = gl_ProjectionMatrix[2].z; float B = gl_ProjectionMatrix[3].z; return B / (A + clipZ); } #ifndef DISABLE_STENCIL_TEXTURE void StencilSoftShadow() { vec2 texSize = u_renderResolution; vec2 pixSize = vec2(1.0, 1.0) / texSize; vec2 baseTC = gl_FragCoord.xy * pixSize; float StTex = float(texture2D( u_stencilTexture, baseTC ).r); float stencil = clamp( 129. - StTex, 0., 1.); float sumWeight = 1.; float LightDist = min(length(lightDir), 1e3); // crutch ! //radius of light source float lightRadius = u_softShadowsRadius; //radius of one-point penumbra at the consided point (in world coordinates) //note that proper formula is: lightRadius * (LightDist - OcclDist) / OcclDist; float blurRadiusWorld = lightRadius * LightDist / 66.6666; //TODO: revert?! //project direction to light onto surface vec3 normal = var_TangentBitangentNormalMatrix[2]; vec3 alongDirW = normalize(lightDir - dot(lightDir, normal) * normal); //get orthogonal direction on surface vec3 orthoDirW = cross(normal, alongDirW); //multiply the two axes by penumbra radius alongDirW *= blurRadiusWorld / max(NdotL, 0.2); //penumbra is longer by (1/cos(a)) in light direction orthoDirW *= blurRadiusWorld; //convert both vectors into clip space (get only X and Y components) vec2 alongDir = (mat3(gl_ModelViewProjectionMatrix) * alongDirW).xy; vec2 orthoDir = (mat3(gl_ModelViewProjectionMatrix) * orthoDirW).xy; //now also get W component from multiplication by gl_ModelViewProjectionMatrix vec3 mvpRow3 = vec3(gl_ModelViewProjectionMatrix[0][3], gl_ModelViewProjectionMatrix[1][3], gl_ModelViewProjectionMatrix[2][3]); float along_w = dot(mvpRow3, alongDirW); float ortho_w = dot(mvpRow3, orthoDirW); //this is perspective correction: it is necessary because W component in clip space also varies //if you remove it and look horizontally parallel to a wall, then vertical shadow boundaries on this wall won't be blurred vec2 thisNdc = (2 * baseTC - vec2(1)); alongDir -= thisNdc * along_w; orthoDir -= thisNdc * ortho_w; //divide by clip W to get NDC coords (screen coords are half of them) alongDir *= gl_FragCoord.w / 2; orthoDir *= gl_FragCoord.w / 2; //Note: if you want to check the math just above, consider how screen position changes when a point moves in specified direction: // F(t) = divideByW(gl_ModelViewProjectionMatrix * (var_Position + dir_world * t)).xy //the converted vector must be equal to the derivative by parameter: // dir_screen = dF/dt (0) //(here [dir_world, dir_screen] are either [alongDirW, alongDir] or [orthoDirW, orthoDir]) //estimate the length of spot ellipse vectors (in pixels) float lenX = length(alongDir * texSize); float lenY = length(orthoDir * texSize); //make sure vectors are sufficiently sampled float avgSampleDistInPixels = 2 * max(1e-3 * texSize.y, 1.0); float oversize = max(lenX, lenY) / (avgSampleDistInPixels * sqrt(0.0 + u_softShadowsQuality)); if (oversize > 1) { alongDir /= oversize; orthoDir /= oversize; } #if ENABLE_ANTI_HALO_PATCH //compute partial derivatives of eye -Z by screen X and Y (normalized) float Z00 = depthToZ(gl_FragCoord.z); vec2 dzdxy = vec2(dFdx(Z00), dFdy(Z00)); //this is a stupid version, which gets derivatives from depth texture: /* float Z00 = depthToZ(texture(u_depthTexture, baseTC).r); float Zp0 = depthToZ(texture(u_depthTexture, baseTC + vec2(pixSize.x, 0)).r); float Zm0 = depthToZ(texture(u_depthTexture, baseTC - vec2(pixSize.x, 0)).r); float Z0p = depthToZ(texture(u_depthTexture, baseTC + vec2(0, pixSize.y)).r); float Z0m = depthToZ(texture(u_depthTexture, baseTC - vec2(0, pixSize.y)).r); float dzdx = (abs(Zp0 - Z00) < abs(Z00 - Zm0) ? Zp0 - Z00 : Z00 - Zm0); float dzdy = (abs(Z0p - Z00) < abs(Z00 - Z0m) ? Z0p - Z00 : Z00 - Z0m); vec2 dzdxy = vec2(dzdx, dzdy);*/ //rescale to derivatives by texture coordinates (not pixels) dzdxy *= texSize; //compute Z derivatives on a theoretical wall visible under 45-degree angle vec2 tanFovHalf = vec2(1.0 / gl_ProjectionMatrix[0][0], 1.0 / gl_ProjectionMatrix[1][1]); vec2 canonDerivs = 2.0 * Z00 * tanFovHalf; #endif for( int i = 0; i < u_softShadowsQuality; i++ ) { vec2 delta = u_softShadowsSamples[i].x * alongDir + u_softShadowsSamples[i].y * orthoDir; vec2 StTc = baseTC + delta; #if ENABLE_ANTI_HALO_PATCH float Zdiff = depthToZ(texture2D(u_depthTexture, StTc).r) - Z00; float tangentZdiff = dot(dzdxy, delta); float deg45diff = dot(canonDerivs, abs(delta)); float weight = float(abs(Zdiff - tangentZdiff) <= abs(tangentZdiff) * 0.5 + deg45diff * 0.2); #else float ZDiff = (gl_FragCoord.z-texture( u_depthTexture, StTc ).r) / gl_FragCoord.w; float weight = 1. / (1. + ZDiff*ZDiff); #endif float StTex = float(texture2D( u_stencilTexture, StTc ).r); stencil += clamp( 129. - StTex, 0., 1. ) * weight; sumWeight += weight; } gl_FragColor.rgb *= stencil / sumWeight; /*vec2 StTc = baseTC + vec2(1, 0) * 1e-2; StTex = texture( u_stencilTexture, StTc ).r; stencil = .25*(128. - StTex); gl_FragColor.rgb = vec3(stencil, -stencil, stencil==0?.3:0); */ } #endif void main() { if (u_advanced == 1.0) gl_FragColor.rgb = advancedInteraction(); else gl_FragColor.rgb = simpleInteraction(); #ifndef DISABLE_STENCIL_TEXTURE if (u_shadows == 1. && u_softShadowsQuality > 0) StencilSoftShadow(); #endif gl_FragColor.a = 1.0; }