The unexpected gain during debugging today. I share it for those who need to study the Playcanvas shader interface for reference.
Vertex shader:
#version 300 es
#define attribute in
#define varying out
#define texture2D texture
#define GL2
#define VERTEXSHADER
varying vec4 vMainShadowUv;
varying vec3 vPositionW;
varying vec3 vNormalW;
varying vec2 vUv0;
attribute vec3 vertex_position;
attribute vec3 vertex_normal;
attribute vec4 vertex_tangent;
attribute vec2 vertex_texCoord0;
attribute vec2 vertex_texCoord1;
attribute vec4 vertex_color;
uniform mat4 matrix_viewProjection;
uniform mat4 matrix_model;
uniform mat3 matrix_normal;
vec3 dPositionW;
mat4 dModelMatrix;
mat3 dNormalMatrix;
vec3 dLightPosW;
vec3 dLightDirNormW;
vec3 dNormalW;
uniform mat4 light0_shadowMatrixVS;
uniform vec3 light0_shadowParamsVS;
uniform vec3 light0_directionVS;
void getLightDirPoint(vec3 lightPosW) {
vec3 lightDirW = vPositionW - lightPosW;
dLightDirNormW = normalize(lightDirW);
dLightPosW = lightPosW;
}
void _getShadowCoordOrtho(mat4 shadowMatrix, vec3 shadowParams, vec3 wPos) {
vec4 projPos = shadowMatrix * vec4(wPos, 1.0);
vMainShadowUv = projPos;
}
void _getShadowCoordPersp(mat4 shadowMatrix, vec3 shadowParams, vec3 wPos) {
vec4 projPos = shadowMatrix * vec4(wPos, 1.0);
vMainShadowUv = projPos;
}
void getShadowCoordOrtho(mat4 shadowMatrix, vec3 shadowParams) {
_getShadowCoordOrtho(shadowMatrix, shadowParams, vPositionW);
}
void getShadowCoordPersp(mat4 shadowMatrix, vec3 shadowParams) {
_getShadowCoordPersp(shadowMatrix, shadowParams, vPositionW);
}
void getShadowCoordPerspNormalOffset(mat4 shadowMatrix, vec3 shadowParams) {
float distScale = abs(dot(vPositionW - dLightPosW, dLightDirNormW)); // fov?
vec3 wPos = vPositionW + dNormalW * shadowParams.y * clamp(1.0 - dot(dNormalW, -dLightDirNormW), 0.0, 1.0) * distScale;
_getShadowCoordPersp(shadowMatrix, shadowParams, wPos);
}
void getShadowCoordOrthoNormalOffset(mat4 shadowMatrix, vec3 shadowParams) {
vec3 wPos = vPositionW + dNormalW * shadowParams.y * clamp(1.0 - dot(dNormalW, -dLightDirNormW), 0.0, 1.0); //0.08
_getShadowCoordOrtho(shadowMatrix, shadowParams, wPos);
}
#ifdef NINESLICED
vec2 getUv0() {
vec2 uv = vertex_position.xz;
// offset inner vertices inside
// (original vertices must be in [-1;1] range)
vec2 positiveUnitOffset = clamp(vertex_position.xz, vec2(0.0), vec2(1.0));
vec2 negativeUnitOffset = clamp(-vertex_position.xz, vec2(0.0), vec2(1.0));
uv += (-positiveUnitOffset * innerOffset.xy + negativeUnitOffset * innerOffset.zw) * vertex_texCoord0.xy;
uv = uv * -0.5 + 0.5;
uv = uv * atlasRect.zw + atlasRect.xy;
vMask = vertex_texCoord0.xy;
return uv;
}
#else
vec2 getUv0() {
return vertex_texCoord0;
}
#endif
#ifdef PIXELSNAP
uniform vec4 uScreenSize;
#endif
#ifdef MORPHING
uniform vec4 morph_weights_a;
uniform vec4 morph_weights_b;
#endif
#ifdef MORPHING_TEXTURE_BASED
uniform vec4 morph_tex_params;
vec2 getTextureMorphCoords() {
float vertexId = morph_vertex_id;
vec2 textureSize = morph_tex_params.xy;
vec2 invTextureSize = morph_tex_params.zw;
// turn vertexId into int grid coordinates
float morphGridV = floor(vertexId * invTextureSize.x);
float morphGridU = vertexId - (morphGridV * textureSize.x);
// convert grid coordinates to uv coordinates with half pixel offset
return (vec2(morphGridU, morphGridV) * invTextureSize) + (0.5 * invTextureSize);
}
#endif
#ifdef MORPHING_TEXTURE_BASED_POSITION
uniform highp sampler2D morphPositionTex;
#endif
mat4 getModelMatrix() {
#ifdef DYNAMICBATCH
return getBoneMatrix(vertex_boneIndices);
#elif defined(SKIN)
return matrix_model * getSkinMatrix(vertex_boneIndices, vertex_boneWeights);
#elif defined(INSTANCING)
return mat4(instance_line1, instance_line2, instance_line3, instance_line4);
#else
return matrix_model;
#endif
}
vec4 getPosition() {
dModelMatrix = getModelMatrix();
vec3 localPos = vertex_position;
#ifdef NINESLICED
// outer and inner vertices are at the same position, scale both
localPos.xz *= outerScale;
// offset inner vertices inside
// (original vertices must be in [-1;1] range)
vec2 positiveUnitOffset = clamp(vertex_position.xz, vec2(0.0), vec2(1.0));
vec2 negativeUnitOffset = clamp(-vertex_position.xz, vec2(0.0), vec2(1.0));
localPos.xz += (-positiveUnitOffset * innerOffset.xy + negativeUnitOffset * innerOffset.zw) * vertex_texCoord0.xy;
vTiledUv = (localPos.xz - outerScale + innerOffset.xy) * -0.5 + 1.0; // uv = local pos - inner corner
localPos.xz *= -0.5; // move from -1;1 to -0.5;0.5
localPos = localPos.xzy;
#endif
#ifdef MORPHING
#ifdef MORPHING_POS03
localPos.xyz += morph_weights_a[0] * morph_pos0;
localPos.xyz += morph_weights_a[1] * morph_pos1;
localPos.xyz += morph_weights_a[2] * morph_pos2;
localPos.xyz += morph_weights_a[3] * morph_pos3;
#endif // MORPHING_POS03
#ifdef MORPHING_POS47
localPos.xyz += morph_weights_b[0] * morph_pos4;
localPos.xyz += morph_weights_b[1] * morph_pos5;
localPos.xyz += morph_weights_b[2] * morph_pos6;
localPos.xyz += morph_weights_b[3] * morph_pos7;
#endif // MORPHING_POS47
#endif // MORPHING
#ifdef MORPHING_TEXTURE_BASED_POSITION
// apply morph offset from texture
vec2 morphUV = getTextureMorphCoords();
vec3 morphPos = texture2D(morphPositionTex, morphUV).xyz;
localPos += morphPos;
#endif
vec4 posW = dModelMatrix * vec4(localPos, 1.0);
#ifdef SCREENSPACE
posW.zw = vec2(0.0, 1.0);
#endif
dPositionW = posW.xyz;
vec4 screenPos;
#ifdef UV1LAYOUT
screenPos = vec4(vertex_texCoord1.xy * 2.0 - 1.0, 0.5, 1);
#else
#ifdef SCREENSPACE
screenPos = posW;
#else
screenPos = matrix_viewProjection * posW;
#endif
#ifdef PIXELSNAP
// snap vertex to a pixel boundary
screenPos.xy = (screenPos.xy * 0.5) + 0.5;
screenPos.xy *= uScreenSize.xy;
screenPos.xy = floor(screenPos.xy);
screenPos.xy *= uScreenSize.zw;
screenPos.xy = (screenPos.xy * 2.0) - 1.0;
#endif
#endif
return screenPos;
}
vec3 getWorldPosition() {
return dPositionW;
}
#ifdef MORPHING_TEXTURE_BASED_NORMAL
uniform highp sampler2D morphNormalTex;
#endif
vec3 getNormal() {
#ifdef SKIN
dNormalMatrix = mat3(dModelMatrix[0].xyz, dModelMatrix[1].xyz, dModelMatrix[2].xyz);
#elif defined(INSTANCING)
dNormalMatrix = mat3(instance_line1.xyz, instance_line2.xyz, instance_line3.xyz);
#else
dNormalMatrix = matrix_normal;
#endif
vec3 tempNormal = vertex_normal;
#ifdef MORPHING
#ifdef MORPHING_NRM03
tempNormal += morph_weights_a[0] * morph_nrm0;
tempNormal += morph_weights_a[1] * morph_nrm1;
tempNormal += morph_weights_a[2] * morph_nrm2;
tempNormal += morph_weights_a[3] * morph_nrm3;
#endif
#ifdef MORPHING_NRM47
tempNormal += morph_weights_b[0] * morph_nrm4;
tempNormal += morph_weights_b[1] * morph_nrm5;
tempNormal += morph_weights_b[2] * morph_nrm6;
tempNormal += morph_weights_b[3] * morph_nrm7;
#endif
#endif
#ifdef MORPHING_TEXTURE_BASED_NORMAL
// apply morph offset from texture
vec2 morphUV = getTextureMorphCoords();
vec3 morphNormal = texture2D(morphNormalTex, morphUV).xyz;
tempNormal += morphNormal;
#endif
return normalize(dNormalMatrix * tempNormal);
}
void main(void) {
gl_Position = getPosition();
vPositionW = getWorldPosition();
vNormalW = dNormalW = getNormal();
dLightDirNormW = light0_directionVS;
getShadowCoordOrthoNormalOffset(light0_shadowMatrixVS, light0_shadowParamsVS);
vec2 uv0 = getUv0();
vUv0 = uv0;
}
Fragment shader:
#version 300 es
#define varying in
out highp vec4 pc_fragColor;
#define gl_FragColor pc_fragColor
#define texture2D texture
#define textureCube texture
#define texture2DProj textureProj
#define texture2DLodEXT textureLod
#define texture2DProjLodEXT textureProjLod
#define textureCubeLodEXT textureLod
#define texture2DGradEXT textureGrad
#define texture2DProjGradEXT textureProjGrad
#define textureCubeGradEXT textureGrad
#define GL2
precision highp float;
#ifdef GL2
precision highp sampler2DShadow;
#endif
varying vec4 vMainShadowUv;
varying vec3 vPositionW;
varying vec3 vNormalW;
varying vec2 vUv0;
uniform vec3 view_position;
uniform vec3 light_globalAmbient;
float square(float x) {
return x*x;
}
float saturate(float x) {
return clamp(x, 0.0, 1.0);
}
vec3 saturate(vec3 x) {
return clamp(x, vec3(0.0), vec3(1.0));
}
vec3 detailMode_mul(vec3 c1, vec3 c2) {
return c1 * c2;
}
vec3 detailMode_add(vec3 c1, vec3 c2) {
return c1 + c2;
}
// https://en.wikipedia.org/wiki/Blend_modes#Screen
vec3 detailMode_screen(vec3 c1, vec3 c2) {
return 1.0 - (1.0 - c1)*(1.0 - c2);
}
// https://en.wikipedia.org/wiki/Blend_modes#Overlay
vec3 detailMode_overlay(vec3 c1, vec3 c2) {
return mix(1.0 - 2.0*(1.0 - c1)*(1.0 - c2), 2.0*c1*c2, step(c1, vec3(0.5)));
}
vec3 detailMode_min(vec3 c1, vec3 c2) {
return min(c1, c2);
}
vec3 detailMode_max(vec3 c1, vec3 c2) {
return max(c1, c2);
}
vec4 dReflection;
vec3 dAlbedo;
vec3 dNormalW;
vec3 dVertexNormalW;
vec3 dViewDirW;
vec3 dReflDirW;
vec3 dDiffuseLight;
vec3 dSpecularLight;
vec3 dLightDirNormW;
vec3 dLightDirW;
vec3 dLightPosW;
vec3 dShadowCoord;
vec3 dSpecularity;
float dGlossiness;
float dAlpha;
float dAtten;
vec4 ccReflection;
vec3 ccNormalW;
vec3 ccSpecularLight;
float ccSpecularity;
float ccGlossiness;
uniform vec3 light0_color;
uniform vec3 light0_direction;
uniform mat4 light0_shadowMatrix;
uniform vec3 light0_shadowParams;
uniform sampler2DShadow light0_shadowMap;
void getNormal() {
dNormalW = normalize(dVertexNormalW);
}
vec3 gammaCorrectInput(vec3 color) {
return pow(color, vec3(2.2));
}
float gammaCorrectInput(float color) {
return pow(color, 2.2);
}
vec4 gammaCorrectInput(vec4 color) {
return vec4(pow(color.rgb, vec3(2.2)), color.a);
}
vec4 texture2DSRGB(sampler2D tex, vec2 uv) {
vec4 rgba = texture2D(tex, uv);
rgba.rgb = gammaCorrectInput(rgba.rgb);
return rgba;
}
vec4 texture2DSRGB(sampler2D tex, vec2 uv, float bias) {
vec4 rgba = texture2D(tex, uv, bias);
rgba.rgb = gammaCorrectInput(rgba.rgb);
return rgba;
}
vec4 textureCubeSRGB(samplerCube tex, vec3 uvw) {
vec4 rgba = textureCube(tex, uvw);
rgba.rgb = gammaCorrectInput(rgba.rgb);
return rgba;
}
vec3 gammaCorrectOutput(vec3 color) {
#ifdef HDR
return color;
#else
color += vec3(0.0000001);
return pow(color, vec3(0.45));
#endif
}
uniform float exposure;
vec3 toneMap(vec3 color) {
return color * exposure;
}
float dBlendModeFogFactor = 1.0;
vec3 addFog(vec3 color) {
return color;
}
vec3 cubeMapProject(vec3 dir) {
return dir;
}
vec3 processEnvironment(vec3 color) {
return color;
}
#undef MAPFLOAT
#undef MAPCOLOR
#undef MAPVERTEX
#undef MAPTEXTURE
#ifdef MAPTEXTURE
uniform sampler2D texture_diffuseDetailMap;
#endif
vec3 addAlbedoDetail(vec3 albedo) {
#ifdef MAPTEXTURE
vec3 albedoDetail = vec3(texture2D(texture_diffuseDetailMap, UV).CH);
return detailMode_mul(albedo, albedoDetail);
#else
return albedo;
#endif
}
#undef MAPFLOAT
#undef MAPCOLOR
#undef MAPVERTEX
#undef MAPTEXTURE
#define MAPTEXTURE
#ifdef MAPCOLOR
uniform vec3 material_diffuse;
#endif
#ifdef MAPTEXTURE
uniform sampler2D texture_diffuseMap;
#endif
void getAlbedo() {
dAlbedo = vec3(1.0);
#ifdef MAPCOLOR
dAlbedo *= material_diffuse.rgb;
#endif
#ifdef MAPTEXTURE
dAlbedo *= gammaCorrectInput(addAlbedoDetail(texture2D(texture_diffuseMap, vUv0).rgb));
#endif
#ifdef MAPVERTEX
dAlbedo *= gammaCorrectInput(saturate(vVertexColor.VC));
#endif
}
#undef MAPFLOAT
#undef MAPCOLOR
#undef MAPVERTEX
#undef MAPTEXTURE
#define MAPTEXTURE
#ifdef MAPFLOAT
uniform float material_opacity;
#endif
#ifdef MAPTEXTURE
uniform sampler2D texture_opacityMap;
#endif
void getOpacity() {
dAlpha = 1.0;
#ifdef MAPFLOAT
dAlpha *= material_opacity;
#endif
#ifdef MAPTEXTURE
dAlpha *= texture2D(texture_opacityMap, vUvNaN).r;
#endif
#ifdef MAPVERTEX
dAlpha *= clamp(vVertexColor.VC, 0.0, 1.0);
#endif
}
#undef MAPFLOAT
#undef MAPCOLOR
#define MAPCOLOR
#undef MAPVERTEX
#undef MAPTEXTURE
#ifdef MAPCOLOR
uniform vec3 material_emissive;
#endif
#ifdef MAPFLOAT
uniform float material_emissiveIntensity;
#endif
#ifdef MAPTEXTURE
uniform sampler2D texture_emissiveMap;
#endif
vec3 getEmission() {
vec3 emission = vec3(1.0);
#ifdef MAPFLOAT
emission *= material_emissiveIntensity;
#endif
#ifdef MAPCOLOR
emission *= material_emissive;
#endif
#ifdef MAPTEXTURE
emission *= texture2DSAMPLE(texture_emissiveMap, UV).CH;
#endif
#ifdef MAPVERTEX
emission *= gammaCorrectInput(saturate(vVertexColor.VC));
#endif
return emission;
}
float antiAliasGlossiness(float power) {
return power;
}
#undef MAPFLOAT
#undef MAPCOLOR
#define MAPCOLOR
#undef MAPVERTEX
#undef MAPTEXTURE
#ifdef MAPCOLOR
uniform vec3 material_specular;
#endif
#ifdef MAPTEXTURE
uniform sampler2D texture_specularMap;
#endif
void getSpecularity() {
dSpecularity = vec3(1.0);
#ifdef MAPCOLOR
dSpecularity *= material_specular;
#endif
#ifdef MAPTEXTURE
dSpecularity *= texture2D(texture_specularMap, UV).CH;
#endif
#ifdef MAPVERTEX
dSpecularity *= saturate(vVertexColor.VC);
#endif
}
#undef MAPFLOAT
#define MAPFLOAT
#undef MAPCOLOR
#undef MAPVERTEX
#undef MAPTEXTURE
#ifdef MAPFLOAT
uniform float material_shininess;
#endif
#ifdef MAPTEXTURE
uniform sampler2D texture_glossMap;
#endif
void getGlossiness() {
dGlossiness = 1.0;
#ifdef MAPFLOAT
dGlossiness *= material_shininess;
#endif
#ifdef MAPTEXTURE
dGlossiness *= texture2D(texture_glossMap, UV).CH;
#endif
#ifdef MAPVERTEX
dGlossiness *= saturate(vVertexColor.VC);
#endif
dGlossiness += 0.0000001;
}
// Schlick's approximation
uniform float material_fresnelFactor; // unused
void getFresnel() {
float fresnel = 1.0 - max(dot(dNormalW, dViewDirW), 0.0);
float fresnel2 = fresnel * fresnel;
fresnel *= fresnel2 * fresnel2;
fresnel *= dGlossiness * dGlossiness;
dSpecularity = dSpecularity + (1.0 - dSpecularity) * fresnel;
#ifdef CLEARCOAT
fresnel = 1.0 - max(dot(ccNormalW, dViewDirW), 0.0);
fresnel2 = fresnel * fresnel;
fresnel *= fresnel2 * fresnel2;
fresnel *= ccGlossiness * ccGlossiness;
ccSpecularity = ccSpecularity + (1.0 - ccSpecularity) * fresnel;
#endif
}
vec3 lessThan2(vec3 a, vec3 b) {
return clamp((b - a)*1000.0, 0.0, 1.0); // softer version
}
#ifndef UNPACKFLOAT
#define UNPACKFLOAT
float unpackFloat(vec4 rgbaDepth) {
const vec4 bitShift = vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0);
return dot(rgbaDepth, bitShift);
}
#endif
// ----- Direct/Spot Sampling -----
#ifdef GL2
float _getShadowPCF3x3(sampler2DShadow shadowMap, vec3 shadowParams) {
float z = dShadowCoord.z;
vec2 uv = dShadowCoord.xy * shadowParams.x; // 1 unit - 1 texel
float shadowMapSizeInv = 1.0 / shadowParams.x;
vec2 base_uv = floor(uv + 0.5);
float s = (uv.x + 0.5 - base_uv.x);
float t = (uv.y + 0.5 - base_uv.y);
base_uv -= vec2(0.5);
base_uv *= shadowMapSizeInv;
float sum = 0.0;
float uw0 = (3.0 - 2.0 * s);
float uw1 = (1.0 + 2.0 * s);
float u0 = (2.0 - s) / uw0 - 1.0;
float u1 = s / uw1 + 1.0;
float vw0 = (3.0 - 2.0 * t);
float vw1 = (1.0 + 2.0 * t);
float v0 = (2.0 - t) / vw0 - 1.0;
float v1 = t / vw1 + 1.0;
u0 = u0 * shadowMapSizeInv + base_uv.x;
v0 = v0 * shadowMapSizeInv + base_uv.y;
u1 = u1 * shadowMapSizeInv + base_uv.x;
v1 = v1 * shadowMapSizeInv + base_uv.y;
sum += uw0 * vw0 * texture(shadowMap, vec3(u0, v0, z));
sum += uw1 * vw0 * texture(shadowMap, vec3(u1, v0, z));
sum += uw0 * vw1 * texture(shadowMap, vec3(u0, v1, z));
sum += uw1 * vw1 * texture(shadowMap, vec3(u1, v1, z));
sum *= 1.0f / 16.0;
return sum;
}
float getShadowPCF3x3(sampler2DShadow shadowMap, vec3 shadowParams) {
return _getShadowPCF3x3(shadowMap, shadowParams);
}
float getShadowSpotPCF3x3(sampler2DShadow shadowMap, vec4 shadowParams) {
return _getShadowPCF3x3(shadowMap, shadowParams.xyz);
}
#else
float _xgetShadowPCF3x3(mat3 depthKernel, sampler2D shadowMap, vec3 shadowParams) {
mat3 shadowKernel;
vec3 shadowCoord = dShadowCoord;
vec3 shadowZ = vec3(shadowCoord.z);
shadowKernel[0] = vec3(greaterThan(depthKernel[0], shadowZ));
shadowKernel[1] = vec3(greaterThan(depthKernel[1], shadowZ));
shadowKernel[2] = vec3(greaterThan(depthKernel[2], shadowZ));
vec2 fractionalCoord = fract( shadowCoord.xy * shadowParams.x );
shadowKernel[0] = mix(shadowKernel[0], shadowKernel[1], fractionalCoord.x);
shadowKernel[1] = mix(shadowKernel[1], shadowKernel[2], fractionalCoord.x);
vec4 shadowValues;
shadowValues.x = mix(shadowKernel[0][0], shadowKernel[0][1], fractionalCoord.y);
shadowValues.y = mix(shadowKernel[0][1], shadowKernel[0][2], fractionalCoord.y);
shadowValues.z = mix(shadowKernel[1][0], shadowKernel[1][1], fractionalCoord.y);
shadowValues.w = mix(shadowKernel[1][1], shadowKernel[1][2], fractionalCoord.y);
return dot( shadowValues, vec4( 1.0 ) ) * 0.25;
}
float _getShadowPCF3x3(sampler2D shadowMap, vec3 shadowParams) {
vec3 shadowCoord = dShadowCoord;
float xoffset = 1.0 / shadowParams.x; // 1/shadow map width
float dx0 = -xoffset;
float dx1 = xoffset;
mat3 depthKernel;
depthKernel[0][0] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(dx0, dx0)));
depthKernel[0][1] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(dx0, 0.0)));
depthKernel[0][2] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(dx0, dx1)));
depthKernel[1][0] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(0.0, dx0)));
depthKernel[1][1] = unpackFloat(texture2D(shadowMap, shadowCoord.xy));
depthKernel[1][2] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(0.0, dx1)));
depthKernel[2][0] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(dx1, dx0)));
depthKernel[2][1] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(dx1, 0.0)));
depthKernel[2][2] = unpackFloat(texture2D(shadowMap, shadowCoord.xy + vec2(dx1, dx1)));
return _xgetShadowPCF3x3(depthKernel, shadowMap, shadowParams);
}
float getShadowPCF3x3(sampler2D shadowMap, vec3 shadowParams) {
return _getShadowPCF3x3(shadowMap, shadowParams);
}
float getShadowSpotPCF3x3(sampler2D shadowMap, vec4 shadowParams) {
return _getShadowPCF3x3(shadowMap, shadowParams.xyz);
}
#endif
// ----- Point Sampling -----
float _getShadowPoint(samplerCube shadowMap, vec4 shadowParams, vec3 dir) {
vec3 tc = normalize(dir);
vec3 tcAbs = abs(tc);
vec4 dirX = vec4(1,0,0, tc.x);
vec4 dirY = vec4(0,1,0, tc.y);
float majorAxisLength = tc.z;
if ((tcAbs.x > tcAbs.y) && (tcAbs.x > tcAbs.z)) {
dirX = vec4(0,0,1, tc.z);
dirY = vec4(0,1,0, tc.y);
majorAxisLength = tc.x;
} else if ((tcAbs.y > tcAbs.x) && (tcAbs.y > tcAbs.z)) {
dirX = vec4(1,0,0, tc.x);
dirY = vec4(0,0,1, tc.z);
majorAxisLength = tc.y;
}
float shadowParamsInFaceSpace = ((1.0/shadowParams.x) * 2.0) * abs(majorAxisLength);
vec3 xoffset = (dirX.xyz * shadowParamsInFaceSpace);
vec3 yoffset = (dirY.xyz * shadowParamsInFaceSpace);
vec3 dx0 = -xoffset;
vec3 dy0 = -yoffset;
vec3 dx1 = xoffset;
vec3 dy1 = yoffset;
mat3 shadowKernel;
mat3 depthKernel;
depthKernel[0][0] = unpackFloat(textureCube(shadowMap, tc + dx0 + dy0));
depthKernel[0][1] = unpackFloat(textureCube(shadowMap, tc + dx0));
depthKernel[0][2] = unpackFloat(textureCube(shadowMap, tc + dx0 + dy1));
depthKernel[1][0] = unpackFloat(textureCube(shadowMap, tc + dy0));
depthKernel[1][1] = unpackFloat(textureCube(shadowMap, tc));
depthKernel[1][2] = unpackFloat(textureCube(shadowMap, tc + dy1));
depthKernel[2][0] = unpackFloat(textureCube(shadowMap, tc + dx1 + dy0));
depthKernel[2][1] = unpackFloat(textureCube(shadowMap, tc + dx1));
depthKernel[2][2] = unpackFloat(textureCube(shadowMap, tc + dx1 + dy1));
vec3 shadowZ = vec3(length(dir) * shadowParams.w + shadowParams.z);
shadowKernel[0] = vec3(lessThan2(depthKernel[0], shadowZ));
shadowKernel[1] = vec3(lessThan2(depthKernel[1], shadowZ));
shadowKernel[2] = vec3(lessThan2(depthKernel[2], shadowZ));
vec2 uv = (vec2(dirX.w, dirY.w) / abs(majorAxisLength)) * 0.5;
vec2 fractionalCoord = fract( uv * shadowParams.x );
shadowKernel[0] = mix(shadowKernel[0], shadowKernel[1], fractionalCoord.x);
shadowKernel[1] = mix(shadowKernel[1], shadowKernel[2], fractionalCoord.x);
vec4 shadowValues;
shadowValues.x = mix(shadowKernel[0][0], shadowKernel[0][1], fractionalCoord.y);
shadowValues.y = mix(shadowKernel[0][1], shadowKernel[0][2], fractionalCoord.y);
shadowValues.z = mix(shadowKernel[1][0], shadowKernel[1][1], fractionalCoord.y);
shadowValues.w = mix(shadowKernel[1][1], shadowKernel[1][2], fractionalCoord.y);
return 1.0 - dot( shadowValues, vec4( 1.0 ) ) * 0.25;
}
float getShadowPointPCF3x3(samplerCube shadowMap, vec4 shadowParams) {
return _getShadowPoint(shadowMap, shadowParams, dLightDirW);
}
void _getShadowCoordOrtho(mat4 shadowMatrix, vec3 shadowParams, vec3 wPos) {
dShadowCoord = (shadowMatrix * vec4(wPos, 1.0)).xyz;
dShadowCoord.z = saturate(dShadowCoord.z) - 0.0001;
#ifdef SHADOWBIAS
dShadowCoord.z += getShadowBias(shadowParams.x, shadowParams.z);
#endif
}
void _getShadowCoordPersp(mat4 shadowMatrix, vec4 shadowParams, vec3 wPos) {
vec4 projPos = shadowMatrix * vec4(wPos, 1.0);
projPos.xy /= projPos.w;
dShadowCoord.xy = projPos.xy;
dShadowCoord.z = length(dLightDirW) * shadowParams.w;
#ifdef SHADOWBIAS
dShadowCoord.z += getShadowBias(shadowParams.x, shadowParams.z);
#endif
}
void getShadowCoordOrtho(mat4 shadowMatrix, vec3 shadowParams) {
_getShadowCoordOrtho(shadowMatrix, shadowParams, vPositionW);
}
void getShadowCoordPersp(mat4 shadowMatrix, vec4 shadowParams) {
_getShadowCoordPersp(shadowMatrix, shadowParams, vPositionW);
}
void getShadowCoordPerspNormalOffset(mat4 shadowMatrix, vec4 shadowParams) {
float distScale = abs(dot(vPositionW - dLightPosW, dLightDirNormW)); // fov?
vec3 wPos = vPositionW + dVertexNormalW * shadowParams.y * clamp(1.0 - dot(dVertexNormalW, -dLightDirNormW), 0.0, 1.0) * distScale;
_getShadowCoordPersp(shadowMatrix, shadowParams, wPos);
}
void getShadowCoordOrthoNormalOffset(mat4 shadowMatrix, vec3 shadowParams) {
vec3 wPos = vPositionW + dVertexNormalW * shadowParams.y * clamp(1.0 - dot(dVertexNormalW, -dLightDirNormW), 0.0, 1.0); //0.08
_getShadowCoordOrtho(shadowMatrix, shadowParams, wPos);
}
void normalOffsetPointShadow(vec4 shadowParams) {
float distScale = length(dLightDirW);
vec3 wPos = vPositionW + dVertexNormalW * shadowParams.y * clamp(1.0 - dot(dVertexNormalW, -dLightDirNormW), 0.0, 1.0) * distScale; //0.02
vec3 dir = wPos - dLightPosW;
dLightDirW = dir;
}
#ifdef GL2
#define SHADOW_SAMPLERVS sampler2DShadow
#else
#define SHADOW_SAMPLERVS sampler2D
#endif
float getShadowPCF3x3VS(SHADOW_SAMPLERVS shadowMap, vec3 shadowParams) {
dShadowCoord = vMainShadowUv.xyz;
dShadowCoord.z = saturate(dShadowCoord.z) - 0.0001;
#ifdef SHADOWBIAS
dShadowCoord.z += getShadowBias(shadowParams.x, shadowParams.z);
#endif
return _getShadowPCF3x3(shadowMap, shadowParams);
}
float getLightDiffuse() {
return max(dot(dNormalW, -dLightDirNormW), 0.0);
}
// Energy-conserving (hopefully) Blinn-Phong
float calcLightSpecular(float tGlossiness, vec3 tNormalW) {
vec3 h = normalize( -dLightDirNormW + dViewDirW );
float nh = max( dot( h, tNormalW ), 0.0 );
float specPow = exp2(tGlossiness * 11.0); // glossiness is linear, power is not; 0 - 2048
specPow = antiAliasGlossiness(specPow);
// Hack: On Mac OS X, calling pow with zero for the exponent generates hideous artifacts so bias up a little
specPow = max(specPow, 0.0001);
return pow(nh, specPow) * (specPow + 2.0) / 8.0;
}
float getLightSpecular() {
return calcLightSpecular(dGlossiness, dNormalW);
}
float getLightSpecularCC() {
return calcLightSpecular(ccGlossiness, ccNormalW);
}
vec3 combineColor() {
return mix(dAlbedo * dDiffuseLight, dSpecularLight + dReflection.rgb * dReflection.a, dSpecularity);
}
void addAmbient() {
dDiffuseLight += light_globalAmbient;
}
void getViewDir() {
dViewDirW = normalize(view_position - vPositionW);
}
void getReflDir() {
dReflDirW = normalize(-reflect(dViewDirW, dNormalW));
}
void main(void) {
dDiffuseLight = vec3(0);
dSpecularLight = vec3(0);
dReflection = vec4(0);
dSpecularity = vec3(0);
#ifdef CLEARCOAT
ccSpecularLight = vec3(0);
ccReflection = vec4(0);
#endif
dVertexNormalW = vNormalW;
getOpacity();
getViewDir();
getNormal();
getReflDir();
getAlbedo();
getSpecularity();
getGlossiness();
getFresnel();
addAmbient();
dLightDirNormW = light0_direction;
dAtten = 1.0;
dAtten *= getLightDiffuse();
dAtten *= getShadowPCF3x3VS(light0_shadowMap, light0_shadowParams);
dDiffuseLight += dAtten * light0_color;
dAtten *= getLightSpecular();
dSpecularLight += dAtten * light0_color;
#ifdef CLEARCOAT
gl_FragColor.rgb = combineColorCC();
#else
gl_FragColor.rgb = combineColor();
#endif
gl_FragColor.rgb += getEmission();
gl_FragColor.rgb = addFog(gl_FragColor.rgb);
#ifndef HDR
gl_FragColor.rgb = toneMap(gl_FragColor.rgb);
gl_FragColor.rgb = gammaCorrectOutput(gl_FragColor.rgb);
#endif
gl_FragColor.a = dAlpha;
}