MetaCore/build_filament/shaders/generated/shaders.bin

3873 lines
137 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#define SPECULAR_AO_OFF 0
#define SPECULAR_AO_SIMPLE 1
#define SPECULAR_AO_BENT_NORMALS 2
float unpack(vec2 depth) {
return (depth.x * (256.0 / 257.0) + depth.y * (1.0 / 257.0));
}
struct SSAOInterpolationCache {
highp vec4 weights;
#if defined(BLEND_MODE_OPAQUE) || defined(BLEND_MODE_MASKED) || defined(MATERIAL_HAS_REFLECTIONS)
highp vec2 uv;
#endif
};
float evaluateSSAO(inout SSAOInterpolationCache cache) {
#if defined(BLEND_MODE_OPAQUE) || defined(BLEND_MODE_MASKED)
if (frameUniforms.aoSamplingQualityAndEdgeDistance < 0.0) {
return 1.0;
}
if (frameUniforms.aoSamplingQualityAndEdgeDistance > 0.0) {
highp vec2 size = vec2(textureSize(sampler0_ssao, 0));
#if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER)
vec4 ao = textureGather(sampler0_ssao, vec3(cache.uv, 0.0), 0);
vec4 dg = textureGather(sampler0_ssao, vec3(cache.uv, 0.0), 1);
vec4 db = textureGather(sampler0_ssao, vec3(cache.uv, 0.0), 2);
#else
vec3 s01 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(0, 1)).rgb;
vec3 s11 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(1, 1)).rgb;
vec3 s10 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(1, 0)).rgb;
vec3 s00 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(0, 0)).rgb;
vec4 ao = vec4(s01.r, s11.r, s10.r, s00.r);
vec4 dg = vec4(s01.g, s11.g, s10.g, s00.g);
vec4 db = vec4(s01.b, s11.b, s10.b, s00.b);
#endif
vec4 depths;
depths.x = unpack(vec2(dg.x, db.x));
depths.y = unpack(vec2(dg.y, db.y));
depths.z = unpack(vec2(dg.z, db.z));
depths.w = unpack(vec2(dg.w, db.w));
depths *= -frameUniforms.cameraFar;
vec2 f = fract(cache.uv * size - 0.5);
vec4 b;
b.x = (1.0 - f.x) * f.y;
b.y = f.x * f.y;
b.z = f.x * (1.0 - f.y);
b.w = (1.0 - f.x) * (1.0 - f.y);
highp mat4 m = getViewFromWorldMatrix();
highp float d = dot(vec3(m[0].z, m[1].z, m[2].z), shading_position) + m[3].z;
highp vec4 w = (vec4(d) - depths) * frameUniforms.aoSamplingQualityAndEdgeDistance;
w = max(vec4(MEDIUMP_FLT_MIN), 1.0 - w * w) * b;
cache.weights = w / (w.x + w.y + w.z + w.w);
return dot(ao, cache.weights);
} else {
return textureLod(sampler0_ssao, vec3(cache.uv, 0.0), 0.0).r;
}
#else
return 1.0;
#endif
}
float SpecularAO_Lagarde(float NoV, float visibility, float roughness) {
return saturate(pow(NoV + visibility, exp2(-16.0 * roughness - 1.0)) - 1.0 + visibility);
}
float sphericalCapsIntersection(float cosCap1, float cosCap2, float cosDistance) {
float r1 = acosFastPositive(cosCap1);
float r2 = acosFastPositive(cosCap2);
float d = acosFast(cosDistance);
if (min(r1, r2) <= max(r1, r2) - d) {
return 1.0 - max(cosCap1, cosCap2);
} else if (r1 + r2 <= d) {
return 0.0;
}
float delta = abs(r1 - r2);
float x = 1.0 - saturate((d - delta) / max(r1 + r2 - delta, 1e-4));
float area = sq(x) * (-2.0 * x + 3.0);
return area * (1.0 - max(cosCap1, cosCap2));
}
float SpecularAO_Cones(vec3 bentNormal, float visibility, float roughness) {
float cosAv = sqrt(1.0 - visibility);
float cosAs = exp2(-3.321928 * sq(roughness));
float cosB = dot(bentNormal, shading_reflected);
float ao = sphericalCapsIntersection(cosAv, cosAs, cosB) / (1.0 - cosAs);
return mix(1.0, ao, smoothstep(0.01, 0.09, roughness));
}
vec3 unpackBentNormal(vec3 bn) {
return bn * 2.0 - 1.0;
}
float specularAO(float NoV, float visibility, float roughness, const in SSAOInterpolationCache cache) {
float specularAO = 1.0;
#if defined(BLEND_MODE_OPAQUE) || defined(BLEND_MODE_MASKED)
#if SPECULAR_AMBIENT_OCCLUSION == SPECULAR_AO_SIMPLE
specularAO = SpecularAO_Lagarde(NoV, visibility, roughness);
#elif SPECULAR_AMBIENT_OCCLUSION == SPECULAR_AO_BENT_NORMALS
# if defined(MATERIAL_HAS_BENT_NORMAL)
specularAO = SpecularAO_Cones(shading_bentNormal, visibility, roughness);
# else
specularAO = SpecularAO_Cones(shading_normal, visibility, roughness);
# endif
#endif
if (frameUniforms.aoBentNormals > 0.0) {
vec3 bn;
if (frameUniforms.aoSamplingQualityAndEdgeDistance > 0.0) {
#if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER)
vec4 bnr = textureGather(sampler0_ssao, vec3(cache.uv, 1.0), 0);
vec4 bng = textureGather(sampler0_ssao, vec3(cache.uv, 1.0), 1);
vec4 bnb = textureGather(sampler0_ssao, vec3(cache.uv, 1.0), 2);
#else
vec3 s01 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(0, 1)).rgb;
vec3 s11 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(1, 1)).rgb;
vec3 s10 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(1, 0)).rgb;
vec3 s00 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(0, 0)).rgb;
vec4 bnr = vec4(s01.r, s11.r, s10.r, s00.r);
vec4 bng = vec4(s01.g, s11.g, s10.g, s00.g);
vec4 bnb = vec4(s01.b, s11.b, s10.b, s00.b);
#endif
bn.r = dot(bnr, cache.weights);
bn.g = dot(bng, cache.weights);
bn.b = dot(bnb, cache.weights);
} else {
bn = textureLod(sampler0_ssao, vec3(cache.uv, 1.0), 0.0).xyz;
}
bn = unpackBentNormal(bn);
bn = normalize(bn);
float ssSpecularAO = SpecularAO_Cones(bn, visibility, roughness);
specularAO = min(specularAO, ssSpecularAO);
}
#endif
return specularAO;
}
#if MULTI_BOUNCE_AMBIENT_OCCLUSION == 1
vec3 gtaoMultiBounce(float visibility, const vec3 albedo) {
vec3 a = 2.0404 * albedo - 0.3324;
vec3 b = -4.7951 * albedo + 0.6417;
vec3 c = 2.7552 * albedo + 0.6903;
return max(vec3(visibility), ((visibility * a + b) * visibility + c) * visibility);
}
#endif
void multiBounceAO(float visibility, const vec3 albedo, inout vec3 color) {
#if MULTI_BOUNCE_AMBIENT_OCCLUSION == 1
color *= gtaoMultiBounce(visibility, albedo);
#endif
}
void multiBounceSpecularAO(float visibility, const vec3 albedo, inout vec3 color) {
#if MULTI_BOUNCE_AMBIENT_OCCLUSION == 1 && SPECULAR_AMBIENT_OCCLUSION != SPECULAR_AO_OFF
color *= gtaoMultiBounce(visibility, albedo);
#endif
}
float singleBounceAO(float visibility) {
#if MULTI_BOUNCE_AMBIENT_OCCLUSION == 1
return 1.0;
#else
return visibility;
#endif
}
#define DIFFUSE_LAMBERT 0
#define DIFFUSE_BURLEY 1
#define SPECULAR_D_GGX 0
#define SPECULAR_D_GGX_ANISOTROPIC 0
#define SPECULAR_D_CHARLIE 0
#define SPECULAR_V_SMITH_GGX 0
#define SPECULAR_V_SMITH_GGX_FAST 1
#define SPECULAR_V_GGX_ANISOTROPIC 2
#define SPECULAR_V_KELEMEN 3
#define SPECULAR_V_NEUBELT 4
#define SPECULAR_F_SCHLICK 0
#define BRDF_DIFFUSE DIFFUSE_LAMBERT
#if FILAMENT_QUALITY < FILAMENT_QUALITY_HIGH
#define BRDF_SPECULAR_D SPECULAR_D_GGX
#define BRDF_SPECULAR_V SPECULAR_V_SMITH_GGX_FAST
#define BRDF_SPECULAR_F SPECULAR_F_SCHLICK
#else
#define BRDF_SPECULAR_D SPECULAR_D_GGX
#define BRDF_SPECULAR_V SPECULAR_V_SMITH_GGX
#define BRDF_SPECULAR_F SPECULAR_F_SCHLICK
#endif
#define BRDF_CLEAR_COAT_D SPECULAR_D_GGX
#define BRDF_CLEAR_COAT_V SPECULAR_V_KELEMEN
#define BRDF_ANISOTROPIC_D SPECULAR_D_GGX_ANISOTROPIC
#define BRDF_ANISOTROPIC_V SPECULAR_V_GGX_ANISOTROPIC
#define BRDF_CLOTH_D SPECULAR_D_CHARLIE
#define BRDF_CLOTH_V SPECULAR_V_NEUBELT
float D_GGX(float roughness, float NoH, const vec3 h) {
#if defined(TARGET_MOBILE)
vec3 NxH = cross(shading_normal, h);
float oneMinusNoHSquared = dot(NxH, NxH);
#else
float oneMinusNoHSquared = 1.0 - NoH * NoH;
#endif
float a = NoH * roughness;
float k = min(roughness / (oneMinusNoHSquared + a * a), 453.5);
float d = k * (k * (1.0 / PI));
return d;
}
float D_GGX_Anisotropic(float at, float ab, float ToH, float BoH, float NoH) {
float a2 = at * ab;
highp vec3 d = vec3(ab * ToH, at * BoH, a2 * NoH);
highp float d2 = dot(d, d);
float b2 = a2 / d2;
return a2 * b2 * b2 * (1.0 / PI);
}
float D_Charlie(float roughness, float NoH) {
float invAlpha = 1.0 / roughness;
float cos2h = NoH * NoH;
float sin2h = max(1.0 - cos2h, 0.0078125);
return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
}
float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {
float a2 = roughness * roughness;
float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
float v = PREVENT_DIV0(0.5, lambdaV + lambdaL, 0.0000077);
return v;
}
float V_SmithGGXCorrelated_Fast(float roughness, float NoV, float NoL) {
float v = PREVENT_DIV0(0.5, mix(2.0 * NoL * NoV, NoL + NoV, roughness), 0.0000077);
return v;
}
float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV,
float ToL, float BoL, float NoV, float NoL) {
float lambdaV = NoL * length(vec3(at * ToV, ab * BoV, NoV));
float lambdaL = NoV * length(vec3(at * ToL, ab * BoL, NoL));
float v = PREVENT_DIV0(0.5, lambdaV + lambdaL, 0.0000077);
return v;
}
float V_Kelemen(float LoH) {
return PREVENT_DIV0(0.25, LoH * LoH, 0.0000039);
}
float V_Neubelt(float NoV, float NoL) {
return PREVENT_DIV0(1.0, 4.0 * (NoL + NoV - NoL * NoV), 0.00001532);
}
vec3 F_Schlick(const vec3 f0, float f90, float VoH) {
return f0 + (f90 - f0) * pow5(1.0 - VoH);
}
vec3 F_Schlick(const vec3 f0, float VoH) {
float f = pow(1.0 - VoH, 5.0);
return f + f0 * (1.0 - f);
}
float F_Schlick(float f0, float f90, float VoH) {
return f0 + (f90 - f0) * pow5(1.0 - VoH);
}
float distribution(float roughness, float NoH, const vec3 h) {
#if BRDF_SPECULAR_D == SPECULAR_D_GGX
return D_GGX(roughness, NoH, h);
#endif
}
float visibility(float roughness, float NoV, float NoL) {
#if BRDF_SPECULAR_V == SPECULAR_V_SMITH_GGX
return V_SmithGGXCorrelated(roughness, NoV, NoL);
#elif BRDF_SPECULAR_V == SPECULAR_V_SMITH_GGX_FAST
return V_SmithGGXCorrelated_Fast(roughness, NoV, NoL);
#endif
}
vec3 fresnel(const vec3 f0, float LoH) {
#if BRDF_SPECULAR_F == SPECULAR_F_SCHLICK
#if FILAMENT_QUALITY == FILAMENT_QUALITY_LOW
return F_Schlick(f0, LoH);
#else
float f90 = saturate(dot(f0, vec3(50.0 * 0.33)));
return F_Schlick(f0, f90, LoH);
#endif
#endif
}
vec3 fresnel(const vec3 f0, const float f90, float LoH) {
#if BRDF_SPECULAR_F == SPECULAR_F_SCHLICK
return F_Schlick(f0, f90, LoH);
#endif
}
float distributionAnisotropic(float at, float ab, float ToH, float BoH, float NoH) {
#if BRDF_ANISOTROPIC_D == SPECULAR_D_GGX_ANISOTROPIC
return D_GGX_Anisotropic(at, ab, ToH, BoH, NoH);
#endif
}
float visibilityAnisotropic(float roughness, float at, float ab,
float ToV, float BoV, float ToL, float BoL, float NoV, float NoL) {
#if BRDF_ANISOTROPIC_V == SPECULAR_V_SMITH_GGX
return V_SmithGGXCorrelated(roughness, NoV, NoL);
#elif BRDF_ANISOTROPIC_V == SPECULAR_V_GGX_ANISOTROPIC
return V_SmithGGXCorrelated_Anisotropic(at, ab, ToV, BoV, ToL, BoL, NoV, NoL);
#endif
}
float distributionClearCoat(float roughness, float NoH, const vec3 h) {
#if BRDF_CLEAR_COAT_D == SPECULAR_D_GGX
return D_GGX(roughness, NoH, h);
#endif
}
float visibilityClearCoat(float LoH) {
#if BRDF_CLEAR_COAT_V == SPECULAR_V_KELEMEN
return V_Kelemen(LoH);
#endif
}
float distributionCloth(float roughness, float NoH) {
#if BRDF_CLOTH_D == SPECULAR_D_CHARLIE
return D_Charlie(roughness, NoH);
#endif
}
float visibilityCloth(float NoV, float NoL) {
#if BRDF_CLOTH_V == SPECULAR_V_NEUBELT
return V_Neubelt(NoV, NoL);
#endif
}
float Fd_Lambert() {
return 1.0 / PI;
}
float Fd_Burley(float roughness, float NoV, float NoL, float LoH) {
float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
float lightScatter = F_Schlick(1.0, f90, NoL);
float viewScatter = F_Schlick(1.0, f90, NoV);
return lightScatter * viewScatter * (1.0 / PI);
}
float Fd_Wrap(float NoL, float w) {
return saturate((NoL + w) / sq(1.0 + w));
}
float diffuse(float roughness, float NoV, float NoL, float LoH) {
#if BRDF_DIFFUSE == DIFFUSE_LAMBERT
return Fd_Lambert();
#elif BRDF_DIFFUSE == DIFFUSE_BURLEY
return Fd_Burley(roughness, NoV, NoL, LoH);
#endif
}
#if defined(FILAMENT_VULKAN_SEMANTICS)
#define LAYOUT_LOCATION(x) layout(location = x)
#else
#define LAYOUT_LOCATION(x)
#endif
#define bool2 bvec2
#define bool3 bvec3
#define bool4 bvec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define float3x3 mat3
#define float4x4 mat4
#if defined(TARGET_VULKAN_ENVIRONMENT) && defined(TARGET_MOBILE)
#define highp_mat4 highp mat4
#else
#define highp_mat4 const highp mat4
#endif
int getEyeIndex() {
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
return instance_index % CONFIG_STEREO_EYE_COUNT;
#elif defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_MULTIVIEW)
# if defined(TARGET_VULKAN_ENVIRONMENT)
return int(gl_ViewIndex);
# else
return int(gl_ViewID_OVR);
# endif
#endif
return 0;
}
highp mat4 getViewFromWorldMatrix() {
return frameUniforms.viewFromWorldMatrix;
}
highp mat4 getWorldFromViewMatrix() {
return frameUniforms.worldFromViewMatrix;
}
highp mat4 getClipFromViewMatrix() {
return frameUniforms.clipFromViewMatrix;
}
highp mat4 getViewFromClipMatrix() {
return frameUniforms.viewFromClipMatrix;
}
#if MATERIAL_FEATURE_LEVEL > 0
highp mat4 getEyeFromViewMatrix() {
return frameUniforms.eyeFromViewMatrix[getEyeIndex()];
}
highp mat4 getEyeFromViewMatrix(int eyeIndex) {
return frameUniforms.eyeFromViewMatrix[eyeIndex];
}
#endif
highp mat4 getClipFromWorldMatrix() {
return frameUniforms.clipFromWorldMatrix[getEyeIndex()];
}
highp mat4 getClipFromWorldMatrix(int eyeIndex) {
return frameUniforms.clipFromWorldMatrix[eyeIndex];
}
highp mat4 getWorldFromClipMatrix() {
return frameUniforms.worldFromClipMatrix;
}
highp mat4 getUserWorldFromWorldMatrix() {
return frameUniforms.userWorldFromWorldMatrix;
}
float getTime() {
return frameUniforms.time;
}
highp vec4 getUserTime() {
return frameUniforms.userTime;
}
highp float getUserTimeMod(float m) {
return mod(mod(frameUniforms.userTime.x, m) + mod(frameUniforms.userTime.y, m), m);
}
highp vec2 uvToRenderTargetUV(const highp vec2 uv) {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
return vec2(uv.x, 1.0 - uv.y);
#else
return uv;
#endif
}
highp vec4 getResolution() {
return frameUniforms.resolution;
}
highp vec3 getWorldCameraPosition() {
return frameUniforms.worldFromViewMatrix[3].xyz;
}
highp vec3 getWorldOffset() {
return getUserWorldFromWorldMatrix()[3].xyz;
}
float getExposure() {
return frameUniforms.exposure;
}
float getEV100() {
return frameUniforms.ev100;
}
highp vec4 getMaterialGlobal0() {
return frameUniforms.custom[0];
}
highp vec4 getMaterialGlobal1() {
return frameUniforms.custom[1];
}
highp vec4 getMaterialGlobal2() {
return frameUniforms.custom[2];
}
highp vec4 getMaterialGlobal3() {
return frameUniforms.custom[3];
}
highp mat4 object_uniforms_worldFromModelMatrix;
highp mat3 object_uniforms_worldFromModelNormalMatrix;
highp int object_uniforms_morphTargetCount;
highp int object_uniforms_flagsChannels;
highp int object_uniforms_objectId;
highp float object_uniforms_userData;
void initObjectUniforms() {
#if defined(FILAMENT_HAS_FEATURE_INSTANCING)
highp int i;
# if defined(MATERIAL_HAS_INSTANCES)
if ((objectUniforms.data[0].flagsChannels & FILAMENT_OBJECT_INSTANCE_BUFFER_BIT) != 0) {
i = logical_instance_index;
} else {
i = 0;
}
# else
i = logical_instance_index;
# endif
#else
const int i = 0;
#endif
object_uniforms_worldFromModelMatrix = objectUniforms.data[i].worldFromModelMatrix;
object_uniforms_worldFromModelNormalMatrix = objectUniforms.data[i].worldFromModelNormalMatrix;
object_uniforms_morphTargetCount = objectUniforms.data[i].morphTargetCount;
object_uniforms_flagsChannels = objectUniforms.data[i].flagsChannels;
object_uniforms_objectId = objectUniforms.data[i].objectId;
object_uniforms_userData = objectUniforms.data[i].userData;
}
#if defined(FILAMENT_HAS_FEATURE_INSTANCING) && defined(MATERIAL_HAS_INSTANCES)
highp int getInstanceIndex() {
return logical_instance_index;
}
#endif
#if CLIENT_MATERIAL_API_LEVEL >= UNSTABLE_MATERIAL_API_LEVEL
highp mat4 getWorldFromModelMatrix() {
return object_uniforms_worldFromModelMatrix;
}
highp mat3 getWorldFromModelNormalMatrix() {
return object_uniforms_worldFromModelNormalMatrix;
}
#endif
float luminance(const vec3 linear) {
return dot(linear, vec3(0.2126, 0.7152, 0.0722));
}
float computePreExposedIntensity(const highp float intensity, const highp float exposure) {
return intensity * exposure;
}
void unpremultiply(inout vec4 color) {
color.rgb /= max(color.a, FLT_EPS);
}
vec3 ycbcrToRgb(float luminance, vec2 cbcr) {
const mat4 ycbcrToRgbTransform = mat4(
1.0000, 1.0000, 1.0000, 0.0000,
0.0000, -0.3441, 1.7720, 0.0000,
1.4020, -0.7141, 0.0000, 0.0000,
-0.7010, 0.5291, -0.8860, 1.0000
);
return (ycbcrToRgbTransform * vec4(luminance, cbcr, 1.0)).rgb;
}
vec3 Inverse_Tonemap_Filmic(const vec3 x) {
return (0.03 - 0.59 * x - sqrt(0.0009 + 1.3702 * x - 1.0127 * x * x)) / (-5.02 + 4.86 * x);
}
vec3 inverseTonemapSRGB(vec3 color) {
color = clamp(color, 0.0, 1.0);
return Inverse_Tonemap_Filmic(pow(color, vec3(2.2)));
}
vec3 inverseTonemap(vec3 linear) {
return Inverse_Tonemap_Filmic(clamp(linear, 0.0, 1.0));
}
vec3 decodeRGBM(vec4 c) {
c.rgb *= (c.a * 16.0);
return c.rgb * c.rgb;
}
highp vec2 getFragCoord(const highp vec2 resolution) {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
return vec2(gl_FragCoord.x, resolution.y - gl_FragCoord.y);
#else
return gl_FragCoord.xy;
#endif
}
vec3 heatmap(float v) {
vec3 r = v * 2.1 - vec3(1.8, 1.14, 0.3);
return 1.0 - r * r;
}
struct Light {
vec4 colorIntensity;
vec3 l;
float attenuation;
highp vec3 worldPosition;
float NoL;
highp vec3 direction;
float zLight;
bool castsShadows;
bool contactShadows;
uint lightType;
int shadowIndex;
int channels;
};
struct PixelParams {
vec3 diffuseColor;
float perceptualRoughness;
float perceptualRoughnessUnclamped;
vec3 f0;
#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR)
float f90;
float specular;
vec3 specularColor;
#endif
float roughness;
vec3 dfg;
vec3 energyCompensation;
#if defined(MATERIAL_HAS_CLEAR_COAT)
float clearCoat;
float clearCoatPerceptualRoughness;
float clearCoatRoughness;
#endif
#if defined(MATERIAL_HAS_SHEEN_COLOR)
vec3 sheenColor;
#if !defined(SHADING_MODEL_CLOTH)
float sheenRoughness;
float sheenPerceptualRoughness;
float sheenScaling;
float sheenDFG;
#endif
#endif
#if defined(MATERIAL_HAS_ANISOTROPY)
vec3 anisotropicT;
vec3 anisotropicB;
float anisotropy;
#endif
#if defined(SHADING_MODEL_SUBSURFACE) || defined(MATERIAL_HAS_REFRACTION)
float thickness;
#endif
#if defined(SHADING_MODEL_SUBSURFACE)
vec3 subsurfaceColor;
float subsurfacePower;
#endif
#if defined(SHADING_MODEL_CLOTH) && defined(MATERIAL_HAS_SUBSURFACE_COLOR)
vec3 subsurfaceColor;
#endif
#if defined(MATERIAL_HAS_REFRACTION)
float etaRI;
float etaIR;
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
float dispersion;
#endif
float transmission;
float uThickness;
vec3 absorption;
#endif
};
float computeMicroShadowing(float NoL, float visibility) {
float aperture = inversesqrt(1.0 - min(visibility, 0.9999));
float microShadow = saturate(NoL * aperture);
return microShadow * microShadow;
}
vec3 getReflectedVector(const PixelParams pixel, const vec3 v, const vec3 n) {
#if defined(MATERIAL_HAS_ANISOTROPY)
vec3 anisotropyDirection = pixel.anisotropy >= 0.0 ? pixel.anisotropicB : pixel.anisotropicT;
vec3 anisotropicTangent = cross(anisotropyDirection, v);
vec3 anisotropicNormal = cross(anisotropicTangent, anisotropyDirection);
float bendFactor = abs(pixel.anisotropy) * saturate(5.0 * pixel.perceptualRoughness);
vec3 bentNormal = normalize(mix(n, anisotropicNormal, bendFactor));
vec3 r = reflect(-v, bentNormal);
#else
vec3 r = reflect(-v, n);
#endif
return r;
}
void getAnisotropyPixelParams(const MaterialInputs material, inout PixelParams pixel) {
#if defined(MATERIAL_HAS_ANISOTROPY)
vec3 direction = material.anisotropyDirection;
pixel.anisotropy = material.anisotropy;
pixel.anisotropicT = normalize(shading_tangentToWorld * direction);
pixel.anisotropicB = normalize(cross(getWorldGeometricNormalVector(), pixel.anisotropicT));
#endif
}
#if defined(TARGET_MOBILE)
#define MIN_PERCEPTUAL_ROUGHNESS 0.089
#define MIN_ROUGHNESS 0.007921
#else
#define MIN_PERCEPTUAL_ROUGHNESS 0.045
#define MIN_ROUGHNESS 0.002025
#endif
#define MIN_N_DOT_V 1e-4
float clampNoV(float NoV) {
return max(NoV, MIN_N_DOT_V);
}
vec3 computeDiffuseColor(const vec4 baseColor, float metallic) {
return baseColor.rgb * (1.0 - metallic);
}
vec3 computeF0(const vec4 baseColor, float metallic, float reflectance) {
return baseColor.rgb * metallic + (reflectance * (1.0 - metallic));
}
float computeDielectricF0(float reflectance) {
return 0.16 * reflectance * reflectance;
}
float computeMetallicFromSpecularColor(const vec3 specularColor) {
return max3(specularColor);
}
float computeRoughnessFromGlossiness(float glossiness) {
return 1.0 - glossiness;
}
float perceptualRoughnessToRoughness(float perceptualRoughness) {
return perceptualRoughness * perceptualRoughness;
}
float roughnessToPerceptualRoughness(float roughness) {
return sqrt(roughness);
}
float iorToF0(float transmittedIor, float incidentIor) {
return sq((transmittedIor - incidentIor) / (transmittedIor + incidentIor));
}
float f0ToIor(float f0) {
float r = sqrt(f0);
return (1.0 + r) / (1.0 - r);
}
vec3 f0ClearCoatToSurface(const vec3 f0) {
#if FILAMENT_QUALITY == FILAMENT_QUALITY_LOW
return saturate(f0 * (f0 * 0.526868 + 0.529324) - 0.0482256);
#else
return saturate(f0 * (f0 * (0.941892 - 0.263008 * f0) + 0.346479) - 0.0285998);
#endif
}
#define PI 3.14159265359
#define HALF_PI 1.570796327
#define MEDIUMP_FLT_MAX 65504.0
#define MEDIUMP_FLT_MIN 0.00006103515625
#ifdef TARGET_MOBILE
#define FLT_EPS MEDIUMP_FLT_MIN
#else
#define FLT_EPS 1e-5
#endif
#define saturate(x) clamp(x, 0.0, 1.0)
#ifdef TARGET_MOBILE
#define PREVENT_DIV0(n, d, magic) ((n) / max(d, magic))
#else
#define PREVENT_DIV0(n, d, magic) ((n) / (d))
#endif
float pow5(float x) {
float x2 = x * x;
return x2 * x2 * x;
}
float sq(float x) {
return x * x;
}
float max3(const vec3 v) {
return max(v.x, max(v.y, v.z));
}
float vmax(const vec2 v) {
return max(v.x, v.y);
}
float vmax(const vec3 v) {
return max(v.x, max(v.y, v.z));
}
float vmax(const vec4 v) {
return max(max(v.x, v.y), max(v.z, v.w));
}
float min3(const vec3 v) {
return min(v.x, min(v.y, v.z));
}
float vmin(const vec2 v) {
return min(v.x, v.y);
}
float vmin(const vec3 v) {
return min(v.x, min(v.y, v.z));
}
float vmin(const vec4 v) {
return min(min(v.x, v.y), min(v.z, v.w));
}
float acosFast(float x) {
float y = abs(x);
float p = -0.1565827 * y + 1.570796;
p *= sqrt(1.0 - y);
return x >= 0.0 ? p : PI - p;
}
float acosFastPositive(float x) {
float p = -0.1565827 * x + 1.570796;
return p * sqrt(1.0 - x);
}
highp vec4 mulMat4x4Float3(const highp mat4 m, const highp vec3 v) {
return v.x * m[0] + (v.y * m[1] + (v.z * m[2] + m[3]));
}
highp vec3 mulMat3x3Float3(const highp mat4 m, const highp vec3 v) {
return v.x * m[0].xyz + (v.y * m[1].xyz + (v.z * m[2].xyz));
}
void toTangentFrame(const highp vec4 q, out highp vec3 n) {
n = vec3( 0.0, 0.0, 1.0) +
vec3( 2.0, -2.0, -2.0) * q.x * q.zwx +
vec3( 2.0, 2.0, -2.0) * q.y * q.wzy;
}
void toTangentFrame(const highp vec4 q, out highp vec3 n, out highp vec3 t) {
toTangentFrame(q, n);
t = vec3( 1.0, 0.0, 0.0) +
vec3(-2.0, 2.0, -2.0) * q.y * q.yxw +
vec3(-2.0, 2.0, 2.0) * q.z * q.zwx;
}
highp mat3 cofactor(const highp mat3 m) {
highp float a = m[0][0];
highp float b = m[1][0];
highp float c = m[2][0];
highp float d = m[0][1];
highp float e = m[1][1];
highp float f = m[2][1];
highp float g = m[0][2];
highp float h = m[1][2];
highp float i = m[2][2];
highp mat3 cof;
cof[0][0] = e * i - f * h;
cof[0][1] = c * h - b * i;
cof[0][2] = b * f - c * e;
cof[1][0] = f * g - d * i;
cof[1][1] = a * i - c * g;
cof[1][2] = c * d - a * f;
cof[2][0] = d * h - e * g;
cof[2][1] = b * g - a * h;
cof[2][2] = a * e - b * d;
return cof;
}
float interleavedGradientNoise(highp vec2 w) {
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
return fract(m.z * fract(dot(w, m.xy)));
}
highp mat3 shading_tangentToWorld;
highp vec3 shading_position;
vec3 shading_view;
#if defined(HAS_ATTRIBUTE_TANGENTS)
vec3 shading_normal;
vec3 shading_geometricNormal;
vec3 shading_reflected;
float shading_NoV;
#if defined(MATERIAL_HAS_BENT_NORMAL)
vec3 shading_bentNormal;
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT)
vec3 shading_clearCoatNormal;
#endif
#endif
highp vec2 shading_normalizedViewportCoord;
#if defined(VARIANT_HAS_SHADOWING)
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
const highp vec3 dir, const highp vec2 b, highp_mat4 lightFromWorldMatrix) {
#if !defined(VARIANT_HAS_VSM)
highp vec3 L_right = vec3(lightFromWorldMatrix[0][0], lightFromWorldMatrix[1][0], lightFromWorldMatrix[2][0]);
highp vec3 L_up = vec3(lightFromWorldMatrix[0][1], lightFromWorldMatrix[1][1], lightFromWorldMatrix[2][1]);
highp float n_Lx = dot(n, L_right);
highp float n_Ly = dot(n, L_up);
p += n * (abs(n_Lx * b.x) + abs(n_Ly * b.y));
#endif
return mulMat4x4Float3(lightFromWorldMatrix, p);
}
#endif
#if defined(VARIANT_HAS_SHADOWING)
struct ShadowData {
highp mat4 lightFromWorldMatrix;
highp vec4 lightFromWorldZ;
highp vec4 scissorNormalized;
mediump float bulbRadiusLs;
mediump float nearOverFarMinusNear;
highp vec2 normalBias;
bool elvsm;
mediump uint layer;
mediump float vsmExponent;
mediump uint reserved2;
};
#endif
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
struct BoneData {
highp mat3x4 transform;
highp float3 cof0;
highp float cof1x;
};
#endif
struct PerRenderableData {
highp mat4 worldFromModelMatrix;
highp mat3 worldFromModelNormalMatrix;
highp int morphTargetCount;
highp int flagsChannels;
highp int objectId;
highp float userData;
#if MATERIAL_FEATURE_LEVEL > 0
highp vec4 reserved[8];
#endif
};
#define FILAMENT_OBJECT_SKINNING_ENABLED_BIT 0x100
#define FILAMENT_OBJECT_MORPHING_POSITION_BIT 0x200
#define FILAMENT_OBJECT_MORPHING_TANGENT_BIT 0x400
#define FILAMENT_OBJECT_MORPHING_CUSTOM_BIT 0x800
#define FILAMENT_OBJECT_CONTACT_SHADOWS_BIT 0x1000
#define FILAMENT_OBJECT_INSTANCE_BUFFER_BIT 0x2000
#if defined(VARIANT_HAS_VSM)
layout(location = 0) out highp vec4 fragColor;
#elif defined(VARIANT_HAS_PICKING)
# if MATERIAL_FEATURE_LEVEL == 0
layout(location = 0) out highp vec4 outPicking;
# else
layout(location = 0) out highp uvec2 outPicking;
# endif
#else
#endif
highp vec4 computeDepthMomentsVSM(const highp float depth);
void main() {
filament_lodBias = frameUniforms.lodBias;
initObjectUniforms();
#if defined(MATERIAL_HAS_CUSTOM_DEPTH) || defined(BLEND_MODE_MASKED) || ((defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)) && defined(MATERIAL_HAS_TRANSPARENT_SHADOW))
MaterialInputs inputs;
initMaterial(inputs);
material(inputs);
float alpha = inputs.baseColor.a;
#if defined(BLEND_MODE_MASKED)
if (alpha < getMaskThreshold()) {
discard;
}
#endif
#if defined(MATERIAL_HAS_TRANSPARENT_SHADOW)
float noise = interleavedGradientNoise(gl_FragCoord.xy);
if (noise >= alpha) {
discard;
}
#endif
#endif
#if defined(VARIANT_HAS_VSM)
highp float depth = vertex_worldPosition.w;
fragColor = computeDepthMomentsVSM(depth);
#elif defined(VARIANT_HAS_PICKING)
#if MATERIAL_FEATURE_LEVEL == 0
outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0;
outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0;
outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0;
outPicking.r = vertex_position.z / vertex_position.w;
#else
outPicking.x = uint(object_uniforms_objectId);
outPicking.y = floatBitsToUint(vertex_position.z / vertex_position.w);
#endif
#else
#endif
}
#if MATERIAL_FEATURE_LEVEL > 0
highp vec4 computeDepthMomentsVSM(const highp float depth) {
float c = frameUniforms.vsmExponent;
highp float MAX_MOMENT = frameUniforms.vsmMaxMoment;
highp float z = exp(c * depth);
highp vec2 m1 = vec2(z, -1.0 / z);
highp vec2 m2 = m1 * m1;
highp float dzdx = dFdx(depth);
highp float dzdy = dFdy(depth);
highp float linearVariance = 0.25 * (dzdx * dzdx + dzdy * dzdy);
highp vec2 analyticVariance = c * c * m2 * linearVariance;
m2 = min(m2 + analyticVariance, MAX_MOMENT);
return vec4(m1.x, m2.x, m1.y, m2.y);
}
#endif
#if MATERIAL_FEATURE_LEVEL > 0
vec4 fog(highp vec3 view, const mediump samplerCube fogColorTexture) {
highp float d = length(view);
if (d < frameUniforms.fogStart) {
return vec4(0);
}
if (d > frameUniforms.fogCutOffDistance) {
return vec4(0);
}
highp vec3 density = frameUniforms.fogDensity;
highp float falloff = frameUniforms.fogHeightFalloff;
highp float fogOpticalPathAtOneMeter = density.z;
highp float fh = falloff * view.y;
if (abs(fh) > 0.00125) {
fogOpticalPathAtOneMeter = (density.z - density.x * exp(density.y - fh)) / fh;
}
highp float fogOpticalPath = fogOpticalPathAtOneMeter * max(d - frameUniforms.fogStart, 0.0);
float fogTransmittance = exp(-fogOpticalPath);
float fogOpacity = min(1.0 - fogTransmittance, frameUniforms.fogMaxOpacity);
vec3 fogColor = frameUniforms.fogColor;
#if MATERIAL_FEATURE_LEVEL > 0
if (frameUniforms.fogColorFromIbl > 0.0) {
float normalizedDepth = d * frameUniforms.fogOneOverFarMinusNear - frameUniforms.fogNearOverFarMinusNear;
lowp vec2 minMaxMip = unpackHalf2x16(frameUniforms.fogMinMaxMip);
lowp float lod = mix(minMaxMip.y, minMaxMip.x, saturate(normalizedDepth));
highp mat3 worldFromUserWorldMatrix = transpose(mat3(frameUniforms.userWorldFromWorldMatrix));
fogColor *= textureLod(fogColorTexture, worldFromUserWorldMatrix * view, lod).rgb;
}
#endif
fogColor *= frameUniforms.iblLuminance * fogOpacity;
if (frameUniforms.fogInscatteringSize > 0.0) {
highp float sunOpticalPath =
fogOpticalPathAtOneMeter * max(d - frameUniforms.fogInscatteringStart, 0.0);
float sunTransmittance = exp(-sunOpticalPath);
vec3 sunColor = frameUniforms.lightColorIntensity.rgb * frameUniforms.lightColorIntensity.w;
float sunAmount = max(dot(normalize(view), frameUniforms.lightDirection), 0.0);
float sunInscattering = pow(sunAmount, frameUniforms.fogInscatteringSize);
fogColor += sunColor * (sunInscattering * (1.0 - sunTransmittance));
}
return vec4(fogColor, fogOpacity);
}
#endif
vec4 fogLinear(highp vec3 view
#if MATERIAL_FEATURE_LEVEL > 0
, const mediump samplerCube fogColorTexture
#endif
)
{
highp float d = length(view);
if (d < frameUniforms.fogStart) {
return vec4(0);
}
if (d > frameUniforms.fogCutOffDistance) {
return vec4(0);
}
highp float A = frameUniforms.fogLinearParams.x;
highp float B = frameUniforms.fogLinearParams.y;
float fogOpacity = saturate(A * d + B);
vec3 fogColor = frameUniforms.fogColor;
#if MATERIAL_FEATURE_LEVEL > 0
if (frameUniforms.fogColorFromIbl > 0.0) {
lowp vec2 minMaxMip = unpackHalf2x16(frameUniforms.fogMinMaxMip);
highp mat3 worldFromUserWorldMatrix = transpose(mat3(frameUniforms.userWorldFromWorldMatrix));
fogColor *= textureLod(fogColorTexture, worldFromUserWorldMatrix * view, minMaxMip.x).rgb;
}
#endif
fogColor *= frameUniforms.iblLuminance * fogOpacity;
return vec4(fogColor, fogOpacity);
}
uvec3 getWorkGroupID() {
return gl_WorkGroupID;
}
uvec3 getWorkGroupCount() {
return gl_NumWorkGroups;
}
uvec3 getLocalInvocationID() {
return gl_LocalInvocationID;
}
uint getLocalInvocationIndex() {
return gl_LocalInvocationIndex;
}
uvec3 getGlobalInvocationID() {
return gl_GlobalInvocationID;
}
float getObjectUserData() {
return object_uniforms_userData;
}
#if defined(HAS_ATTRIBUTE_COLOR)
vec4 getColor() {
return vertex_color;
}
#endif
#if defined(HAS_ATTRIBUTE_UV0)
highp vec2 getUV0() {
return vertex_uv01.xy;
}
#endif
#if defined(HAS_ATTRIBUTE_UV1)
highp vec2 getUV1() {
return vertex_uv01.zw;
}
#endif
#if defined(BLEND_MODE_MASKED)
float getMaskThreshold() {
return materialParams._maskThreshold;
}
#endif
highp mat3 getWorldTangentFrame() {
return shading_tangentToWorld;
}
highp vec3 getWorldPosition() {
return shading_position;
}
highp vec3 getUserWorldPosition() {
return mulMat4x4Float3(getUserWorldFromWorldMatrix(), getWorldPosition()).xyz;
}
vec3 getWorldViewVector() {
return shading_view;
}
bool isPerspectiveProjection() {
return frameUniforms.clipFromViewMatrix[2].w != 0.0;
}
#if defined(HAS_ATTRIBUTE_TANGENTS)
vec3 getWorldNormalVector() {
return shading_normal;
}
vec3 getWorldGeometricNormalVector() {
return shading_geometricNormal;
}
vec3 getWorldReflectedVector() {
return shading_reflected;
}
float getNdotV() {
return shading_NoV;
}
#endif
highp vec3 getNormalizedPhysicalViewportCoord() {
return vec3(shading_normalizedViewportCoord, gl_FragCoord.z);
}
highp vec3 getNormalizedViewportCoord() {
highp vec2 scale = frameUniforms.logicalViewportScale;
highp vec2 offset = frameUniforms.logicalViewportOffset;
highp vec2 logicalUv = shading_normalizedViewportCoord * scale + offset;
return vec3(logicalUv, gl_FragCoord.z);
}
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DYNAMIC_LIGHTING)
highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLight) {
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
highp vec2 bias = shadowUniforms.shadows[index].normalBias * zLight;
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
dir, bias, lightFromWorldMatrix);
}
#endif
#if defined(MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY)
bool isDoubleSided() {
return materialParams._doubleSided;
}
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
int getShadowCascade() {
highp float z = mulMat4x4Float3(getViewFromWorldMatrix(), getWorldPosition()).z;
ivec4 greaterZ = ivec4(greaterThan(frameUniforms.cascadeSplits, vec4(z)));
int cascadeCount = frameUniforms.cascades & 0xF;
return clamp(greaterZ.x + greaterZ.y + greaterZ.z + greaterZ.w, 0, cascadeCount - 1);
}
highp vec4 getCascadeLightSpacePosition(int cascade) {
if (cascade == 0) {
return vertex_lightSpacePosition;
}
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
frameUniforms.lightDirection,
shadowUniforms.shadows[cascade].normalBias,
shadowUniforms.shadows[cascade].lightFromWorldMatrix);
}
#endif
#if CLIENT_MATERIAL_API_LEVEL < UNSTABLE_MATERIAL_API_LEVEL
mat4 getWorldFromModelMatrix() {
return object_uniforms_worldFromModelMatrix;
}
mat3 getWorldFromModelNormalMatrix() {
return object_uniforms_worldFromModelNormalMatrix;
}
#endif
float getObjectUserData() {
return object_uniforms_userData;
}
#if MATERIAL_FEATURE_LEVEL > 0
int getVertexIndex() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
return gl_VertexIndex;
#else
return gl_VertexID;
#endif
}
#endif
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
#define MAX_SKINNING_BUFFER_WIDTH 2048u
vec3 mulBoneNormal(vec3 n, uint j) {
highp mat3 cof;
highp float a = bonesUniforms.bones[j].transform[0][0];
highp float b = bonesUniforms.bones[j].transform[0][1];
highp float c = bonesUniforms.bones[j].transform[0][2];
highp float d = bonesUniforms.bones[j].transform[1][0];
highp float e = bonesUniforms.bones[j].transform[1][1];
highp float f = bonesUniforms.bones[j].transform[1][2];
highp float g = bonesUniforms.bones[j].transform[2][0];
highp float h = bonesUniforms.bones[j].transform[2][1];
highp float i = bonesUniforms.bones[j].transform[2][2];
cof[0] = bonesUniforms.bones[j].cof0;
cof[1].xyz = vec3(
bonesUniforms.bones[j].cof1x,
a * i - c * g,
c * d - a * f);
cof[2].xyz = vec3(
d * h - e * g,
b * g - a * h,
a * e - b * d);
return normalize(cof * n);
}
vec3 mulBoneVertex(vec3 v, uint i) {
highp mat4x3 m = transpose(bonesUniforms.bones[i].transform);
return v.x * m[0].xyz + (v.y * m[1].xyz + (v.z * m[2].xyz + m[3].xyz));
}
void skinPosition(inout vec3 p, const uvec4 ids, const vec4 weights) {
if (weights.w >= 0.0) {
p = weights.x * mulBoneVertex(p, uint(ids.x))
+ weights.y * mulBoneVertex(p, uint(ids.y))
+ weights.z * mulBoneVertex(p, uint(ids.z))
+ weights.w * mulBoneVertex(p, uint(ids.w));
return;
}
vec3 posSum = weights.x * mulBoneVertex(p, uint(ids.x));
posSum += weights.y * mulBoneVertex(p, uint(ids.y));
posSum += weights.z * mulBoneVertex(p, uint(ids.z));
uint pairIndex = uint(-weights.w - 1.);
uint pairStop = pairIndex + uint(ids.w - 3u);
for (uint i = pairIndex; i < pairStop; ++i) {
ivec2 texcoord = ivec2(i % MAX_SKINNING_BUFFER_WIDTH, i / MAX_SKINNING_BUFFER_WIDTH);
vec2 indexWeight = texelFetch(sampler1_indicesAndWeights, texcoord, 0).rg;
posSum += mulBoneVertex(p, uint(indexWeight.r)) * indexWeight.g;
}
p = posSum;
}
void skinNormal(inout vec3 n, const uvec4 ids, const vec4 weights) {
if (weights.w >= 0.0) {
n = weights.x * mulBoneNormal(n, uint(ids.x))
+ weights.y * mulBoneNormal(n, uint(ids.y))
+ weights.z * mulBoneNormal(n, uint(ids.z))
+ weights.w * mulBoneNormal(n, uint(ids.w));
return;
}
vec3 normSum = weights.x * mulBoneNormal(n, uint(ids.x));
normSum += weights.y * mulBoneNormal(n, uint(ids.y));
normSum += weights.z * mulBoneNormal(n, uint(ids.z));
uint pairIndex = uint(-weights.w - 1.);
uint pairStop = pairIndex + uint(ids.w - 3u);
for (uint i = pairIndex; i < pairStop; i = i + 1u) {
ivec2 texcoord = ivec2(i % MAX_SKINNING_BUFFER_WIDTH, i / MAX_SKINNING_BUFFER_WIDTH);
vec2 indexWeight = texelFetch(sampler1_indicesAndWeights, texcoord, 0).rg;
normSum += mulBoneNormal(n, uint(indexWeight.r)) * indexWeight.g;
}
n = normSum;
}
void skinNormalTangent(inout vec3 n, inout vec3 t, const uvec4 ids, const vec4 weights) {
if (weights.w >= 0.0) {
n = weights.x * mulBoneNormal(n, uint(ids.x))
+ weights.y * mulBoneNormal(n, uint(ids.y))
+ weights.z * mulBoneNormal(n, uint(ids.z))
+ weights.w * mulBoneNormal(n, uint(ids.w));
t = weights.x * mulBoneNormal(t, uint(ids.x))
+ weights.y * mulBoneNormal(t, uint(ids.y))
+ weights.z * mulBoneNormal(t, uint(ids.z))
+ weights.w * mulBoneNormal(t, uint(ids.w));
return;
}
vec3 normSum = weights.x * mulBoneNormal(n, uint(ids.x));
normSum += weights.y * mulBoneNormal(n, uint(ids.y)) ;
normSum += weights.z * mulBoneNormal(n, uint(ids.z));
vec3 tangSum = weights.x * mulBoneNormal(t, uint(ids.x));
tangSum += weights.y * mulBoneNormal(t, uint(ids.y));
tangSum += weights.z * mulBoneNormal(t, uint(ids.z));
uint pairIndex = uint(-weights.w - 1.);
uint pairStop = pairIndex + uint(ids.w - 3u);
for (uint i = pairIndex; i < pairStop; i = i + 1u) {
ivec2 texcoord = ivec2(i % MAX_SKINNING_BUFFER_WIDTH, i / MAX_SKINNING_BUFFER_WIDTH);
vec2 indexWeight = texelFetch(sampler1_indicesAndWeights, texcoord, 0).rg;
normSum += mulBoneNormal(n, uint(indexWeight.r)) * indexWeight.g;
tangSum += mulBoneNormal(t, uint(indexWeight.r)) * indexWeight.g;
}
n = normSum;
t = tangSum;
}
#define MAX_MORPH_TARGET_BUFFER_WIDTH 2048
#if CLIENT_MATERIAL_API_LEVEL >= UNSTABLE_MATERIAL_API_LEVEL
void morphData2(inout vec2 v, highp sampler2DArray data) {
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
if (w != 0.0) {
texcoord.z = i;
v += w * texelFetch(data, texcoord, 0).xy;
}
}
}
void morphData3(inout vec3 v, highp sampler2DArray data) {
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
if (w != 0.0) {
texcoord.z = i;
v += w * texelFetch(data, texcoord, 0).xyz;
}
}
}
void morphData4(inout vec4 v, highp sampler2DArray data) {
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
if (w != 0.0) {
texcoord.z = i;
v += w * texelFetch(data, texcoord, 0);
}
}
}
void morphPosition(inout vec4 p) {
morphData4(p, sampler1_positions);
}
#else
void morphPosition(inout vec4 p) {
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
if (w != 0.0) {
texcoord.z = i;
p += w * texelFetch(sampler1_positions, texcoord, 0);
}
}
}
#endif
void morphNormal(inout vec3 n) {
vec3 baseNormal = n;
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
if (w != 0.0) {
texcoord.z = i;
ivec4 tangent = texelFetch(sampler1_tangents, texcoord, 0);
vec3 normal;
toTangentFrame(float4(tangent) * (1.0 / 32767.0), normal);
n += w * (normal - baseNormal);
}
}
}
#endif
vec4 getPosition() {
vec4 pos = mesh_position;
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_MORPHING_POSITION_BIT) != 0) {
#if defined(LEGACY_MORPHING)
pos += morphingUniforms.weights[0] * mesh_custom0;
pos += morphingUniforms.weights[1] * mesh_custom1;
pos += morphingUniforms.weights[2] * mesh_custom2;
pos += morphingUniforms.weights[3] * mesh_custom3;
#else
morphPosition(pos);
#endif
}
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0) {
skinPosition(pos.xyz, mesh_bone_indices, mesh_bone_weights);
}
#endif
return pos;
}
#if defined(HAS_ATTRIBUTE_CUSTOM0)
vec4 getCustom0() { return mesh_custom0; }
#endif
#if defined(HAS_ATTRIBUTE_CUSTOM1)
vec4 getCustom1() { return mesh_custom1; }
#endif
#if defined(HAS_ATTRIBUTE_CUSTOM2)
vec4 getCustom2() { return mesh_custom2; }
#endif
#if defined(HAS_ATTRIBUTE_CUSTOM3)
vec4 getCustom3() { return mesh_custom3; }
#endif
#if defined(HAS_ATTRIBUTE_CUSTOM4)
vec4 getCustom4() { return mesh_custom4; }
#endif
#if defined(HAS_ATTRIBUTE_CUSTOM5)
vec4 getCustom5() { return mesh_custom5; }
#endif
#if defined(HAS_ATTRIBUTE_CUSTOM6)
vec4 getCustom6() { return mesh_custom6; }
#endif
#if defined(HAS_ATTRIBUTE_CUSTOM7)
vec4 getCustom7() { return mesh_custom7; }
#endif
vec4 computeWorldPosition() {
#if defined(VERTEX_DOMAIN_OBJECT)
mat4 transform = getWorldFromModelMatrix();
vec3 position = getPosition().xyz;
return mulMat4x4Float3(transform, position);
#elif defined(VERTEX_DOMAIN_WORLD)
return vec4(getPosition().xyz, 1.0);
#elif defined(VERTEX_DOMAIN_VIEW)
mat4 transform = getWorldFromViewMatrix();
vec3 position = getPosition().xyz;
return mulMat4x4Float3(transform, position);
#elif defined(VERTEX_DOMAIN_DEVICE)
mat4 transform = getWorldFromClipMatrix();
vec4 p = getPosition();
p.z = p.z * -0.5 + 0.5;
vec4 position = transform * p;
const highp float ALMOST_ZERO_FLT = 1.08420217249e-19;
if (abs(position.w) < ALMOST_ZERO_FLT) {
position.w = position.w < 0.0 ? -ALMOST_ZERO_FLT : ALMOST_ZERO_FLT;
}
return position * (1.0 / position.w);
#else
#error Unknown Vertex Domain
#endif
}
#if FILAMENT_QUALITY >= FILAMENT_QUALITY_HIGH
#define SUN_AS_AREA_LIGHT
#endif
vec3 sampleSunAreaLight(const vec3 lightDirection) {
#if defined(SUN_AS_AREA_LIGHT)
if (frameUniforms.sun.w >= 0.0) {
float LoR = dot(lightDirection, shading_reflected);
float d = frameUniforms.sun.x;
highp vec3 s = shading_reflected - LoR * lightDirection;
return LoR < d ?
normalize(lightDirection * d + normalize(s) * frameUniforms.sun.y) : shading_reflected;
}
#endif
return lightDirection;
}
Light getDirectionalLight() {
Light light;
light.colorIntensity = frameUniforms.lightColorIntensity;
light.l = sampleSunAreaLight(frameUniforms.lightDirection);
light.attenuation = 1.0;
light.NoL = saturate(dot(shading_normal, light.l));
light.channels = frameUniforms.lightChannels & 0xFF;
return light;
}
void evaluateDirectionalLight(const MaterialInputs material,
const PixelParams pixel, inout vec3 color) {
Light light = getDirectionalLight();
int channels = object_uniforms_flagsChannels & 0xFF;
if ((light.channels & channels) == 0) {
return;
}
#if defined(MATERIAL_CAN_SKIP_LIGHTING)
if (light.NoL <= 0.0) {
return;
}
#endif
float visibility = 1.0;
#if defined(VARIANT_HAS_SHADOWING)
if (light.NoL > 0.0) {
float ssContactShadowOcclusion = 0.0;
int cascade = getShadowCascade();
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
highp vec4 shadowPosition = getShadowPosition(cascade);
visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0);
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
applyShadowStrength(visibility, material.shadowStrength);
#endif
#if defined (FILAMENT_SHADOW_FAR_ATTENUATION)
highp vec3 v = getWorldPosition() - getWorldCameraPosition();
highp float z = dot(transpose(getViewFromWorldMatrix())[2].xyz, v);
highp vec2 p = frameUniforms.shadowFarAttenuationParams;
visibility = 1.0 - ((1.0 - visibility) * saturate(p.x - z * z * p.y));
#endif
}
if ((frameUniforms.directionalShadows & 0x2) != 0 && visibility > 0.0) {
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0) {
ssContactShadowOcclusion = screenSpaceContactShadow(light.l);
}
}
visibility *= 1.0 - ssContactShadowOcclusion;
#if defined(MATERIAL_HAS_AMBIENT_OCCLUSION)
visibility *= computeMicroShadowing(light.NoL, material.ambientOcclusion);
#endif
#if defined(MATERIAL_CAN_SKIP_LIGHTING)
if (visibility <= 0.0) {
return;
}
#endif
}
#endif
#if defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING)
color.rgb += customSurfaceShading(material, pixel, light, visibility);
#else
color.rgb += surfaceShading(pixel, light, visibility);
#endif
}
#define IBL_INTEGRATION_PREFILTERED_CUBEMAP 0
#define IBL_INTEGRATION_IMPORTANCE_SAMPLING 1
#define IBL_INTEGRATION IBL_INTEGRATION_PREFILTERED_CUBEMAP
#define IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT 64
vec3 decodeDataForIBL(const vec4 data) {
return data.rgb;
}
vec3 PrefilteredDFG_LUT(float lod, float NoV) {
return textureLod(sampler0_iblDFG, vec2(NoV, lod), 0.0).rgb;
}
vec3 prefilteredDFG(float perceptualRoughness, float NoV) {
return PrefilteredDFG_LUT(perceptualRoughness, NoV);
}
vec3 Irradiance_SphericalHarmonics(const vec3 n) {
vec3 sphericalHarmonics = frameUniforms.iblSH[0];
if (CONFIG_SH_BANDS_COUNT >= 2) {
sphericalHarmonics +=
frameUniforms.iblSH[1] * (n.y)
+ frameUniforms.iblSH[2] * (n.z)
+ frameUniforms.iblSH[3] * (n.x);
}
if (CONFIG_SH_BANDS_COUNT >= 3) {
sphericalHarmonics +=
frameUniforms.iblSH[4] * (n.y * n.x)
+ frameUniforms.iblSH[5] * (n.y * n.z)
+ frameUniforms.iblSH[6] * (3.0 * n.z * n.z - 1.0)
+ frameUniforms.iblSH[7] * (n.z * n.x)
+ frameUniforms.iblSH[8] * (n.x * n.x - n.y * n.y);
}
return max(sphericalHarmonics, 0.0);
}
vec3 Irradiance_RoughnessOne(const vec3 n) {
return decodeDataForIBL(textureLod(sampler0_iblSpecular, n, frameUniforms.iblRoughnessOneLevel));
}
vec3 diffuseIrradiance(const vec3 n) {
vec3 irradianceRoughnessOne;
if (CONFIG_STATIC_TEXTURE_TARGET_WORKAROUND) {
irradianceRoughnessOne = Irradiance_RoughnessOne(n);
}
if (frameUniforms.iblSH[0].x == 65504.0) {
#if FILAMENT_QUALITY < FILAMENT_QUALITY_HIGH
if (CONFIG_STATIC_TEXTURE_TARGET_WORKAROUND) {
return irradianceRoughnessOne;
} else {
return Irradiance_RoughnessOne(n);
}
#else
ivec2 s = textureSize(sampler0_iblSpecular, int(frameUniforms.iblRoughnessOneLevel));
float du = 1.0 / float(s.x);
float dv = 1.0 / float(s.y);
vec3 m0 = normalize(cross(n, vec3(0.0, 1.0, 0.0)));
vec3 m1 = cross(m0, n);
vec3 m0du = m0 * du;
vec3 m1dv = m1 * dv;
vec3 c;
c = Irradiance_RoughnessOne(n - m0du - m1dv);
c += Irradiance_RoughnessOne(n + m0du - m1dv);
c += Irradiance_RoughnessOne(n + m0du + m1dv);
c += Irradiance_RoughnessOne(n - m0du + m1dv);
return c * 0.25;
#endif
} else {
return Irradiance_SphericalHarmonics(n);
}
}
float perceptualRoughnessToLod(float perceptualRoughness) {
return frameUniforms.iblRoughnessOneLevel * perceptualRoughness * (2.0 - perceptualRoughness);
}
vec3 prefilteredRadiance(const vec3 r, float lod) {
return decodeDataForIBL(textureLod(sampler0_iblSpecular, r, lod));
}
vec3 prefilteredRadiance(const vec3 r, float roughness, float offset) {
float lod = frameUniforms.iblRoughnessOneLevel * roughness;
return decodeDataForIBL(textureLod(sampler0_iblSpecular, r, lod + offset));
}
vec3 getSpecularDominantDirection(const vec3 n, const vec3 r, float roughness) {
return mix(r, n, roughness * roughness);
}
vec3 specularDFG(const PixelParams pixel) {
#if defined(SHADING_MODEL_CLOTH)
return pixel.f0 * pixel.dfg.z;
#else
#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR)
return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0) * pixel.specular;
#else
return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0);
#endif
#endif
}
vec3 getReflectedVector(const PixelParams pixel, const vec3 n) {
#if defined(MATERIAL_HAS_ANISOTROPY)
vec3 r = getReflectedVector(pixel, shading_view, n);
#else
vec3 r = shading_reflected;
#endif
return getSpecularDominantDirection(n, r, pixel.roughness);
}
#if IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING
vec2 hammersley(uint index) {
const uint numSamples = uint(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT);
const float invNumSamples = 1.0 / float(numSamples);
const float tof = 0.5 / float(0x80000000U);
uint bits = index;
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return vec2(float(index) * invNumSamples, float(bits) * tof);
}
vec3 importanceSamplingNdfDggx(vec2 u, float roughness) {
float a2 = roughness * roughness;
float phi = 2.0 * PI * u.x;
float cosTheta2 = (1.0 - u.y) / (1.0 + (a2 - 1.0) * u.y);
float cosTheta = sqrt(cosTheta2);
float sinTheta = sqrt(1.0 - cosTheta2);
return vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
}
vec3 hemisphereCosSample(vec2 u) {
float phi = 2.0 * PI * u.x;
float cosTheta2 = 1.0 - u.y;
float cosTheta = sqrt(cosTheta2);
float sinTheta = sqrt(1.0 - cosTheta2);
return vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
}
vec3 importanceSamplingVNdfDggx(vec2 u, float roughness, vec3 v) {
float alpha = roughness;
v = normalize(vec3(alpha * v.x, alpha * v.y, v.z));
vec3 up = abs(v.z) < 0.9999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 t = normalize(cross(up, v));
vec3 b = cross(t, v);
float a = 1.0 / (1.0 + v.z);
float r = sqrt(u.x);
float phi = (u.y < a) ? u.y / a * PI : PI + (u.y - a) / (1.0 - a) * PI;
float p1 = r * cos(phi);
float p2 = r * sin(phi) * ((u.y < a) ? 1.0 : v.z);
vec3 h = p1 * t + p2 * b + sqrt(max(0.0, 1.0 - p1*p1 - p2*p2)) * v;
h = normalize(vec3(alpha * h.x, alpha * h.y, max(0.0, h.z)));
return h;
}
float prefilteredImportanceSampling(float ipdf, float omegaP) {
const float numSamples = float(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT);
const float invNumSamples = 1.0 / float(numSamples);
const float K = 4.0;
float omegaS = invNumSamples * ipdf;
float mipLevel = log2(K * omegaS / omegaP) * 0.5;
return mipLevel;
}
vec3 isEvaluateSpecularIBL(const PixelParams pixel, const vec3 n, const vec3 v, const float NoV) {
const int numSamples = IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT;
const float invNumSamples = 1.0 / float(numSamples);
const vec3 up = vec3(0.0, 0.0, 1.0);
mat3 T;
T[0] = normalize(cross(up, n));
T[1] = cross(n, T[0]);
T[2] = n;
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
float a = 2.0 * PI * fract(m.z * fract(dot(gl_FragCoord.xy, m.xy)));
float c = cos(a);
float s = sin(a);
mat3 R;
R[0] = vec3( c, s, 0);
R[1] = vec3(-s, c, 0);
R[2] = vec3( 0, 0, 1);
T *= R;
float roughness = pixel.roughness;
float dim = float(textureSize(sampler0_iblSpecular, 0).x);
float omegaP = (4.0 * PI) / (6.0 * dim * dim);
vec3 indirectSpecular = vec3(0.0);
for (int i = 0; i < numSamples; i++) {
vec2 u = hammersley(i);
vec3 h = T * importanceSamplingNdfDggx(u, roughness);
vec3 l = getReflectedVector(pixel, v, h);
float NoL = saturate(dot(n, l));
if (NoL > 0.0) {
float NoH = dot(n, h);
float LoH = saturate(dot(l, h));
float ipdf = (4.0 * LoH) / (D_GGX(roughness, NoH, h) * NoH);
float mipLevel = prefilteredImportanceSampling(ipdf, omegaP);
vec3 L = decodeDataForIBL(textureLod(sampler0_iblSpecular, l, mipLevel));
float D = distribution(roughness, NoH, h);
float V = visibility(roughness, NoV, NoL);
vec3 F = fresnel(pixel.f0, LoH);
vec3 Fr = F * (D * V * NoL * ipdf * invNumSamples);
indirectSpecular += (Fr * L);
}
}
return indirectSpecular;
}
vec3 isEvaluateDiffuseIBL(const PixelParams pixel, vec3 n, vec3 v) {
const int numSamples = IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT;
const float invNumSamples = 1.0 / float(numSamples);
const vec3 up = vec3(0.0, 0.0, 1.0);
mat3 T;
T[0] = normalize(cross(up, n));
T[1] = cross(n, T[0]);
T[2] = n;
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
float a = 2.0 * PI * fract(m.z * fract(dot(gl_FragCoord.xy, m.xy)));
float c = cos(a);
float s = sin(a);
mat3 R;
R[0] = vec3( c, s, 0);
R[1] = vec3(-s, c, 0);
R[2] = vec3( 0, 0, 1);
T *= R;
float dim = float(textureSize(sampler0_iblSpecular, 0).x);
float omegaP = (4.0 * PI) / (6.0 * dim * dim);
vec3 indirectDiffuse = vec3(0.0);
for (int i = 0; i < numSamples; i++) {
vec2 u = hammersley(i);
vec3 h = T * hemisphereCosSample(u);
vec3 l = getReflectedVector(pixel, v, h);
float NoL = saturate(dot(n, l));
if (NoL > 0.0) {
float ipdf = PI / NoL;
float mipLevel = prefilteredImportanceSampling(ipdf, omegaP) + 1.0;
vec3 L = decodeDataForIBL(textureLod(sampler0_iblSpecular, l, mipLevel));
indirectDiffuse += L;
}
}
return indirectDiffuse * invNumSamples;
}
void isEvaluateClearCoatIBL(const PixelParams pixel, float specularAO, inout vec3 Fd, inout vec3 Fr) {
#if defined(MATERIAL_HAS_CLEAR_COAT)
#if defined(MATERIAL_HAS_NORMAL) || defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
float clearCoatNoV = clampNoV(dot(shading_clearCoatNormal, shading_view));
vec3 clearCoatNormal = shading_clearCoatNormal;
#else
float clearCoatNoV = shading_NoV;
vec3 clearCoatNormal = shading_normal;
#endif
float Fc = F_Schlick(0.04, 1.0, clearCoatNoV) * pixel.clearCoat;
float attenuation = 1.0 - Fc;
Fd *= attenuation;
Fr *= attenuation;
PixelParams p;
p.perceptualRoughness = pixel.clearCoatPerceptualRoughness;
p.f0 = vec3(0.04);
p.roughness = perceptualRoughnessToRoughness(p.perceptualRoughness);
#if defined(MATERIAL_HAS_ANISOTROPY)
p.anisotropy = 0.0;
#endif
vec3 clearCoatLobe = isEvaluateSpecularIBL(p, clearCoatNormal, shading_view, clearCoatNoV);
Fr += clearCoatLobe * (specularAO * pixel.clearCoat);
#endif
}
#endif
void evaluateClothIndirectDiffuseBRDF(const PixelParams pixel, inout float diffuse) {
#if defined(SHADING_MODEL_CLOTH)
#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
diffuse *= Fd_Wrap(shading_NoV, 0.5);
#endif
#endif
}
void evaluateSheenIBL(const PixelParams pixel, float diffuseAO,
const in SSAOInterpolationCache cache, inout vec3 Fd, inout vec3 Fr) {
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE)
#if defined(MATERIAL_HAS_SHEEN_COLOR)
Fd *= pixel.sheenScaling;
Fr *= pixel.sheenScaling;
vec3 reflectance = pixel.sheenDFG * pixel.sheenColor;
reflectance *= specularAO(shading_NoV, diffuseAO, pixel.sheenRoughness, cache);
Fr += reflectance * prefilteredRadiance(shading_reflected,
perceptualRoughnessToLod(pixel.sheenPerceptualRoughness));
#endif
#endif
}
void evaluateClearCoatIBL(const PixelParams pixel, float diffuseAO,
const in SSAOInterpolationCache cache, inout vec3 Fd, inout vec3 Fr) {
#if IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING
float specularAO = specularAO(shading_NoV, diffuseAO, pixel.clearCoatRoughness, cache);
isEvaluateClearCoatIBL(pixel, specularAO, Fd, Fr);
return;
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT)
if (pixel.clearCoat > 0.0) {
#if defined(MATERIAL_HAS_NORMAL) || defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
float clearCoatNoV = clampNoV(dot(shading_clearCoatNormal, shading_view));
vec3 clearCoatR = reflect(-shading_view, shading_clearCoatNormal);
#else
float clearCoatNoV = shading_NoV;
vec3 clearCoatR = shading_reflected;
#endif
float Fc = F_Schlick(0.04, 1.0, clearCoatNoV) * pixel.clearCoat;
float attenuation = 1.0 - Fc;
Fd *= attenuation;
Fr *= attenuation;
float specularAO = specularAO(clearCoatNoV, diffuseAO, pixel.clearCoatRoughness, cache);
Fr += prefilteredRadiance(clearCoatR,
perceptualRoughnessToLod(pixel.clearCoatPerceptualRoughness)) * (specularAO * Fc);
}
#endif
}
void evaluateSubsurfaceIBL(const PixelParams pixel, const vec3 diffuseIrradiance,
inout vec3 Fd, inout vec3 Fr) {
#if defined(SHADING_MODEL_SUBSURFACE)
vec3 viewIndependent = diffuseIrradiance;
vec3 viewDependent = prefilteredRadiance(-shading_view, pixel.roughness, 1.0 + pixel.thickness);
float attenuation = (1.0 - pixel.thickness) / (2.0 * PI);
Fd += pixel.subsurfaceColor * (viewIndependent + viewDependent) * attenuation;
#elif defined(SHADING_MODEL_CLOTH) && defined(MATERIAL_HAS_SUBSURFACE_COLOR)
Fd *= saturate(pixel.subsurfaceColor + shading_NoV);
#endif
}
#if defined(MATERIAL_HAS_REFRACTION)
struct Refraction {
vec3 position;
vec3 direction;
float d;
};
void refractedRaySolidSphere(const vec3 r_in, float NoR_in, float sin2Theta_in,
float etaIR, float etaRI, float thickness, const vec3 n, out Refraction ray) {
float k = 1.0 - etaIR * etaIR * sin2Theta_in;
vec3 rr = etaIR * r_in - (etaIR * NoR_in + sqrt(max(k, 0.0))) * n;
float NoR = dot(n, rr);
float d = thickness * -NoR;
ray.d = d;
ray.position = shading_position + rr * d;
vec3 n1 = normalize(NoR * rr - n * 0.5);
ray.direction = refract(rr, n1, etaRI);
}
void refractionSolidSphere(float etaIR, float etaRI, float thickness,
const vec3 n, vec3 r, out Refraction ray) {
float NoR_in = dot(n, r);
float sin2Theta_in = 1.0 - NoR_in * NoR_in;
refractedRaySolidSphere(r, NoR_in, sin2Theta_in, etaIR, etaRI, thickness, n, ray);
}
void refractionSolidBox(float etaIR, float thickness,
const vec3 n, vec3 r, out Refraction ray) {
vec3 rr = refract(r, n, etaIR);
float NoR = dot(n, rr);
float d = thickness / max(-NoR, 0.001);
ray.position = vec3(shading_position + rr * d);
ray.direction = r;
ray.d = d;
#if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
float envDistance = 10.0;
ray.direction = normalize((ray.position - shading_position) + ray.direction * envDistance);
#endif
}
void refractionThinSphere(float etaIR, float uThickness,
const vec3 n, vec3 r, out Refraction ray) {
float d = 0.0;
#if defined(MATERIAL_HAS_MICRO_THICKNESS)
vec3 rr = refract(r, n, etaIR);
float NoR = dot(n, rr);
d = uThickness / max(-NoR, 0.001);
ray.position = vec3(shading_position + rr * d);
#else
ray.position = vec3(shading_position);
#endif
ray.direction = r;
ray.d = d;
}
vec3 evaluateRefraction(const PixelParams pixel, const vec3 n0, const float lod,
const float etaIR, const float etaRI) {
Refraction ray;
#if REFRACTION_TYPE == REFRACTION_TYPE_SOLID
refractionSolidSphere(etaIR, etaRI, pixel.thickness, n0, -shading_view, ray);
#elif REFRACTION_TYPE == REFRACTION_TYPE_THIN
refractionThinSphere(etaIR, pixel.uThickness, n0, -shading_view, ray);
#else
# error invalid REFRACTION_TYPE
#endif
#if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
vec3 t = prefilteredRadiance(ray.direction, lod) * frameUniforms.iblLuminance;
#else
highp vec4 p = mulMat4x4Float3(getClipFromWorldMatrix(), ray.position);
vec2 uv = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5);
vec3 t = textureLod(sampler0_ssr, vec3(uv, 0.0), lod).rgb;
#endif
#if defined(MATERIAL_HAS_ABSORPTION)
vec3 T = saturate(exp(-pixel.absorption * ray.d));
t *= T;
#endif
return t;
}
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
vec3 calculateDispersion(const PixelParams pixel, const vec3 n0, const float lod) {
const mat3 K0 = mat3(
0.00581637, 0.02312851, 0.01689631,
-0.11782236, 0.11316202, 0.11098148,
-0.45422013, 0.04493517, 0.98249798
);
const mat3 K1 = mat3(
0.14291703, 0.10429778, -0.01556522,
-0.27560148, 0.57678541, -0.06412244,
0.06839811, 0.02732891, 0.01602064
);
const mat3 K2 = mat3(
0.70106120, -0.09440402, -0.00241699,
0.29545674, 0.29931852, -0.04351961,
0.31884400, -0.05627069, 0.00083808
);
const mat3 K3 = mat3(
0.15020522, -0.03302213, 0.00108589,
0.09796715, 0.01073410, -0.00333946,
0.06697807, -0.01599341, 0.00064333
);
const float offsets[4] = float[](0.70795215, 0.24790980, 0.00000000, -0.29204785);
float n_base = pixel.etaRI;
float P = pixel.dispersion / 20.0;
float dispFactor = P * (n_base - 1.0);
float jitter = (interleavedGradientNoise(gl_FragCoord.xy + vec2(frameUniforms.temporalNoise)) - 0.5)
* 0.35 * dispFactor;
float nd0 = n_base + dispFactor * offsets[0] + jitter;
float nd1 = n_base + dispFactor * offsets[1] + jitter;
float nd2 = n_base + dispFactor * offsets[2] + jitter;
float nd3 = n_base + dispFactor * offsets[3] + jitter;
Refraction r0, r1, r2, r3;
vec3 r_in = -shading_view;
float NoR_in = dot(n0, r_in);
float sin2Theta_in = 1.0 - NoR_in * NoR_in;
refractedRaySolidSphere(r_in, NoR_in, sin2Theta_in, 1.0 / nd0, nd0, pixel.thickness, n0, r0);
refractedRaySolidSphere(r_in, NoR_in, sin2Theta_in, 1.0 / nd1, nd1, pixel.thickness, n0, r1);
refractedRaySolidSphere(r_in, NoR_in, sin2Theta_in, 1.0 / nd2, nd2, pixel.thickness, n0, r2);
refractedRaySolidSphere(r_in, NoR_in, sin2Theta_in, 1.0 / nd3, nd3, pixel.thickness, n0, r3);
vec3 s0, s1, s2, s3;
#if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
s0 = prefilteredRadiance(r0.direction, lod);
s1 = prefilteredRadiance(r1.direction, lod);
s2 = prefilteredRadiance(r2.direction, lod);
s3 = prefilteredRadiance(r3.direction, lod);
float ibl = frameUniforms.iblLuminance;
s0 *= ibl;
s1 *= ibl;
s2 *= ibl;
s3 *= ibl;
#else
highp mat4 clipFromWorld = getClipFromWorldMatrix();
vec4 p0 = mulMat4x4Float3(clipFromWorld, r0.position);
vec4 p1 = mulMat4x4Float3(clipFromWorld, r1.position);
vec4 p2 = mulMat4x4Float3(clipFromWorld, r2.position);
vec4 p3 = mulMat4x4Float3(clipFromWorld, r3.position);
vec2 uv0 = uvToRenderTargetUV(p0.xy * (0.5 / p0.w) + 0.5);
vec2 uv1 = uvToRenderTargetUV(p1.xy * (0.5 / p1.w) + 0.5);
vec2 uv2 = uvToRenderTargetUV(p2.xy * (0.5 / p2.w) + 0.5);
vec2 uv3 = uvToRenderTargetUV(p3.xy * (0.5 / p3.w) + 0.5);
s0 = textureLod(sampler0_ssr, vec3(uv0, 0.0), lod).rgb;
s1 = textureLod(sampler0_ssr, vec3(uv1, 0.0), lod).rgb;
s2 = textureLod(sampler0_ssr, vec3(uv2, 0.0), lod).rgb;
s3 = textureLod(sampler0_ssr, vec3(uv3, 0.0), lod).rgb;
#endif
#if defined(MATERIAL_HAS_ABSORPTION)
vec3 T = saturate(exp(-pixel.absorption * r1.d));
s0 *= T;
s1 *= T;
s2 *= T;
s3 *= T;
#endif
return max(K0 * s0 + K1 * s1 + K2 * s2 + K3 * s3, 0.0);
}
#endif
vec3 evaluateRefraction(const PixelParams pixel, const vec3 n0, vec3 E) {
vec3 Ft;
float perceptualRoughness = mix(pixel.perceptualRoughnessUnclamped, 0.0, saturate(pixel.etaIR * 3.0 - 2.0));
#if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
float lod = perceptualRoughnessToLod(perceptualRoughness);
#else
const float invLog2sqrt5 = 0.8614;
float lod = max(0.0, (2.0 * log2(perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
#endif
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
Ft = calculateDispersion(pixel, n0, lod);
#else
Ft = evaluateRefraction(pixel, n0, lod, pixel.etaIR, pixel.etaRI);
#endif
#if REFRACTION_TYPE == REFRACTION_TYPE_THIN
E *= 1.0 + pixel.transmission * (1.0 - E.g) / (1.0 + E.g);
#endif
Ft *= 1.0 - E;
Ft *= pixel.diffuseColor;
return Ft;
}
#endif
void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout vec3 color) {
vec3 Fr = vec3(0.0);
SSAOInterpolationCache interpolationCache;
#if defined(BLEND_MODE_OPAQUE) || defined(BLEND_MODE_MASKED) || defined(MATERIAL_HAS_REFLECTIONS)
interpolationCache.uv = uvToRenderTargetUV(getNormalizedPhysicalViewportCoord().xy);
#endif
#if defined(MATERIAL_HAS_REFLECTIONS)
vec4 ssrFr = vec4(0.0);
#if defined(BLEND_MODE_OPAQUE) || defined(BLEND_MODE_MASKED)
if (frameUniforms.ssrDistance > 0.0) {
const float maxPerceptualRoughness = sqrt(0.5);
if (pixel.perceptualRoughness < maxPerceptualRoughness) {
const float invLog2sqrt5 = 0.8614;
float d = -mulMat4x4Float3(getViewFromWorldMatrix(), getWorldPosition()).z;
float lod = max(0.0, (log2(pixel.roughness / d) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
ssrFr = textureLod(sampler0_ssr, vec3(interpolationCache.uv, 1.0), lod);
}
}
#else
#endif
#else
const vec4 ssrFr = vec4(0.0);
#endif
#if IBL_INTEGRATION == IBL_INTEGRATION_PREFILTERED_CUBEMAP
vec3 E = specularDFG(pixel);
if (ssrFr.a < 1.0) {
vec3 r = getReflectedVector(pixel, shading_normal);
Fr = E * prefilteredRadiance(r, perceptualRoughnessToLod(pixel.perceptualRoughness));
}
#elif IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING
vec3 E = vec3(0.0);
if (ssrFr.a < 1.0) {
Fr = isEvaluateSpecularIBL(pixel, shading_normal, shading_view, shading_NoV);
}
#endif
float ssao = evaluateSSAO(interpolationCache);
float diffuseAO = min(material.ambientOcclusion, ssao);
float specularAO = specularAO(shading_NoV, diffuseAO, pixel.roughness, interpolationCache);
vec3 specularSingleBounceAO = singleBounceAO(specularAO) * pixel.energyCompensation;
Fr *= specularSingleBounceAO;
#if defined(MATERIAL_HAS_REFLECTIONS)
ssrFr.rgb *= specularSingleBounceAO;
#endif
float diffuseBRDF = singleBounceAO(diffuseAO);
evaluateClothIndirectDiffuseBRDF(pixel, diffuseBRDF);
#if defined(MATERIAL_HAS_BENT_NORMAL)
vec3 diffuseNormal = shading_bentNormal;
#else
vec3 diffuseNormal = shading_normal;
#endif
#if IBL_INTEGRATION == IBL_INTEGRATION_PREFILTERED_CUBEMAP
vec3 diffuseIrradiance = diffuseIrradiance(diffuseNormal);
#elif IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING
vec3 diffuseIrradiance = isEvaluateDiffuseIBL(pixel, diffuseNormal, shading_view);
#endif
vec3 Fd = pixel.diffuseColor * diffuseIrradiance * (1.0 - E) * diffuseBRDF;
evaluateSubsurfaceIBL(pixel, diffuseIrradiance, Fd, Fr);
multiBounceAO(diffuseAO, pixel.diffuseColor, Fd);
multiBounceSpecularAO(specularAO, pixel.f0, Fr);
evaluateSheenIBL(pixel, diffuseAO, interpolationCache, Fd, Fr);
evaluateClearCoatIBL(pixel, diffuseAO, interpolationCache, Fd, Fr);
Fr *= frameUniforms.iblLuminance;
Fd *= frameUniforms.iblLuminance;
#if defined(MATERIAL_HAS_REFRACTION)
vec3 Ft = evaluateRefraction(pixel, shading_normal, E);
Ft *= pixel.transmission;
Fd *= (1.0 - pixel.transmission);
#endif
#if defined(MATERIAL_HAS_REFLECTIONS)
Fr = Fr * (1.0 - ssrFr.a) + (E * ssrFr.rgb);
#endif
color.rgb += Fr + Fd;
#if defined(MATERIAL_HAS_REFRACTION)
color.rgb += Ft;
#endif
}
#if defined(MATERIAL_HAS_REFLECTIONS)
highp float linearizeDepth(highp float depth) {
const highp float preventDiv0 = 1.0 / 16777216.0;
highp mat4 p = getViewFromClipMatrix();
return (depth * p[2].z + p[3].z) / max(depth * p[2].w + p[3].w, preventDiv0);
}
void swap(inout highp float a, inout highp float b) {
highp float temp = a;
a = b;
b = temp;
}
highp float distanceSquared(highp vec2 a, highp vec2 b) {
a -= b;
return dot(a, a);
}
bool traceScreenSpaceRay(const highp vec3 vsOrigin, const highp vec3 vsDirection,
highp_mat4 uvFromViewMatrix, const highp sampler2D vsZBuffer,
const float vsZThickness, const highp float nearPlaneZ, const float stride,
const float jitterFraction, const highp float maxSteps, const float maxRayTraceDistance,
out highp vec2 hitPixel, out highp vec3 vsHitPoint) {
highp float rayLength = ((vsOrigin.z + vsDirection.z * maxRayTraceDistance) > nearPlaneZ) ?
(nearPlaneZ - vsOrigin.z) / vsDirection.z : maxRayTraceDistance;
highp vec3 vsEndPoint = vsDirection * rayLength + vsOrigin;
highp vec4 H0 = mulMat4x4Float3(uvFromViewMatrix, vsOrigin);
highp vec4 H1 = mulMat4x4Float3(uvFromViewMatrix, vsEndPoint);
highp float k0 = 1.0 / H0.w;
highp float k1 = 1.0 / H1.w;
highp vec3 Q0 = vsOrigin * k0;
highp vec3 Q1 = vsEndPoint * k1;
highp vec2 P0 = H0.xy * k0;
highp vec2 P1 = H1.xy * k1;
hitPixel = vec2(-1.0, -1.0);
P1 += vec2((distanceSquared(P0, P1) < 0.0001) ? 0.01 : 0.0);
highp vec2 delta = P1 - P0;
bool permute = false;
if (abs(delta.x) < abs(delta.y)) {
permute = true;
delta = delta.yx;
P1 = P1.yx;
P0 = P0.yx;
}
float stepDirection = sign(delta.x);
highp float invdx = stepDirection / delta.x;
highp vec2 dP = vec2(stepDirection, invdx * delta.y);
highp vec3 dQ = (Q1 - Q0) * invdx;
highp float dk = (k1 - k0) * invdx;
dP *= stride; dQ *= stride; dk *= stride;
P0 += dP * jitterFraction; Q0 += dQ * jitterFraction; k0 += dk * jitterFraction;
highp vec3 Q = Q0;
highp float k = k0;
highp float prevZMaxEstimate = vsOrigin.z;
highp float stepCount = 0.0;
highp float rayZMax = prevZMaxEstimate;
highp float rayZMin = prevZMaxEstimate;
highp float sceneZMax = rayZMax + 1e4;
highp float end = P1.x * stepDirection;
for (highp vec2 P = P0;
((P.x * stepDirection) <= end) &&
(stepCount < maxSteps) &&
((rayZMax < sceneZMax - vsZThickness) ||
(rayZMin > sceneZMax)) &&
(sceneZMax != 0.0);
P += dP, Q.z += dQ.z, k += dk, stepCount += 1.0) {
hitPixel = permute ? P.yx : P;
rayZMin = prevZMaxEstimate;
rayZMax = (dQ.z * 0.5 + Q.z) / (dk * 0.5 + k);
prevZMaxEstimate = rayZMax;
if (rayZMin > rayZMax) { swap(rayZMin, rayZMax); }
sceneZMax = linearizeDepth(texelFetch(vsZBuffer, int2(hitPixel), 0).r);
}
Q.xy += dQ.xy * stepCount;
vsHitPoint = Q * (1.0 / k);
return (rayZMax >= sceneZMax - vsZThickness) && (rayZMin <= sceneZMax);
}
highp mat4 scaleMatrix(const highp float x, const highp float y) {
mat4 m = mat4(1.0);
m[0].x = x;
m[1].y = y;
m[2].z = 1.0;
m[3].w = 1.0;
return m;
}
vec4 evaluateScreenSpaceReflections(const highp vec3 wsRayDirection) {
vec4 Fr = vec4(0.0);
highp vec3 wsRayStart = shading_position + frameUniforms.ssrBias * wsRayDirection;
highp vec3 vsOrigin = mulMat4x4Float3(getViewFromWorldMatrix(), wsRayStart).xyz;
highp vec3 vsDirection = mulMat3x3Float3(getViewFromWorldMatrix(), wsRayDirection);
float vsZThickness = frameUniforms.ssrThickness;
highp float nearPlaneZ = -frameUniforms.nearOverFarMinusNear / frameUniforms.oneOverFarMinusNear;
float stride = frameUniforms.ssrStride;
highp vec2 fragCoord = gl_FragCoord.xy;
fragCoord += vec2(frameUniforms.temporalNoise);
float jitterFraction = interleavedGradientNoise(fragCoord);
float maxRayTraceDistance = frameUniforms.ssrDistance;
highp vec2 res = vec2(textureSize(sampler0_structure, 0).xy);
highp mat4 uvFromViewMatrix =
scaleMatrix(res.x, res.y) *
frameUniforms.ssrUvFromViewMatrix;
highp float maxSteps = float(max(res.x, res.y));
highp vec2 hitPixel;
highp vec3 vsHitPoint;
if (traceScreenSpaceRay(vsOrigin, vsDirection, uvFromViewMatrix, sampler0_structure,
vsZThickness, nearPlaneZ, stride, jitterFraction, maxSteps,
maxRayTraceDistance, hitPixel, vsHitPoint)) {
highp vec4 reprojected = mulMat4x4Float3(frameUniforms.ssrReprojection, vsHitPoint);
reprojected.xy *= (1.0 / reprojected.w);
const float fadeRateEdge = 12.0;
const float fadeRateDistance = 4.0;
vec2 edgeFactor = max(fadeRateEdge * abs(reprojected.xy - 0.5) - (fadeRateEdge * 0.5 - 1.0), 0.0);
float fade = saturate(1.0 - dot(edgeFactor, edgeFactor));
float t = distance(vsOrigin, vsHitPoint) / maxRayTraceDistance;
fade *= saturate(fadeRateDistance - fadeRateDistance * t);
fade *= (1.0 - max(0.0, vsDirection.z));
Fr = vec4(textureLod(sampler0_ssr, reprojected.xy, 0.0).rgb * fade, fade);
}
return Fr;
}
#endif
#define FROXEL_BUFFER_WIDTH_SHIFT 6u
#define FROXEL_BUFFER_WIDTH (1u << FROXEL_BUFFER_WIDTH_SHIFT)
#define FROXEL_BUFFER_WIDTH_MASK (FROXEL_BUFFER_WIDTH - 1u)
#define LIGHT_TYPE_POINT 0u
#define LIGHT_TYPE_SPOT 1u
struct FroxelParams {
uint recordOffset;
uint count;
};
uvec3 getFroxelCoords(const highp vec3 fragCoords) {
uvec3 froxelCoord;
froxelCoord.xy = uvec2(fragCoords.xy * frameUniforms.froxelCountXY);
highp float viewSpaceNormalizedZ = frameUniforms.zParams.x * fragCoords.z + frameUniforms.zParams.y;
float zSliceCount = frameUniforms.zParams.w;
float sliceZWithoutOffset = log2(viewSpaceNormalizedZ) * frameUniforms.zParams.z;
froxelCoord.z = uint(clamp(sliceZWithoutOffset + zSliceCount, 0.0, zSliceCount - 1.0));
return froxelCoord;
}
uint getFroxelIndex(const highp vec3 fragCoords) {
uvec3 froxelCoord = getFroxelCoords(fragCoords);
return froxelCoord.x * frameUniforms.fParams.x +
froxelCoord.y * frameUniforms.fParams.y +
froxelCoord.z * frameUniforms.fParams.z;
}
ivec2 getFroxelTexCoord(uint froxelIndex) {
return ivec2(froxelIndex & FROXEL_BUFFER_WIDTH_MASK, froxelIndex >> FROXEL_BUFFER_WIDTH_SHIFT);
}
FroxelParams getFroxelParams(const uint froxelIndex) {
uint w = froxelIndex >> 2u;
uint c = froxelIndex & 0x3u;
highp uvec4 d = froxelsUniforms.records[w];
highp uint f = d[c];
FroxelParams froxel;
froxel.recordOffset = f >> 16u;
froxel.count = f & 0xFFu;
return froxel;
}
uint getLightIndex(const uint index) {
uint v = index >> 4u;
uint c = (index >> 2u) & 0x3u;
uint s = (index & 0x3u) * 8u;
highp uvec4 d = froxelRecordUniforms.records[v];
return (d[c] >> s) & 0xFFu;
}
float getSquareFalloffAttenuation(float distanceSquare, float falloff) {
float factor = distanceSquare * falloff;
float smoothFactor = saturate(1.0 - factor * factor);
return smoothFactor * smoothFactor;
}
float getDistanceAttenuation(const highp vec3 posToLight, float falloff) {
float distanceSquare = dot(posToLight, posToLight);
float attenuation = getSquareFalloffAttenuation(distanceSquare, falloff);
highp vec3 v = getWorldPosition() - getWorldCameraPosition();
attenuation *= saturate(frameUniforms.lightFarAttenuationParams.x - dot(v, v) * frameUniforms.lightFarAttenuationParams.y);
return attenuation / max(distanceSquare, 1e-4);
}
float getAngleAttenuation(const highp vec3 lightDir, const highp vec3 l, const highp vec2 scaleOffset) {
float cd = dot(lightDir, l);
float attenuation = saturate(cd * scaleOffset.x + scaleOffset.y);
return attenuation * attenuation;
}
Light getLight(const uint lightIndex) {
highp mat4 data = lightsUniforms.lights[lightIndex];
highp vec4 positionFalloff = data[0];
highp vec3 direction = data[1].xyz;
vec4 colorIES = vec4(
unpackHalf2x16(floatBitsToUint(data[2][0])),
unpackHalf2x16(floatBitsToUint(data[2][1]))
);
highp vec2 scaleOffset = data[2].zw;
highp float intensity = data[3][1];
highp uint typeShadow = floatBitsToUint(data[3][2]);
highp uint channels = floatBitsToUint(data[3][3]);
highp vec3 worldPosition = getWorldPosition();
highp vec3 posToLight = positionFalloff.xyz - worldPosition;
Light light;
light.colorIntensity.rgb = colorIES.rgb;
light.colorIntensity.w = computePreExposedIntensity(intensity, frameUniforms.exposure);
light.l = normalize(posToLight);
light.attenuation = getDistanceAttenuation(posToLight, positionFalloff.w);
light.direction = direction;
light.NoL = saturate(dot(shading_normal, light.l));
light.worldPosition = positionFalloff.xyz;
light.channels = int(channels);
light.contactShadows = bool(typeShadow & 0x10u);
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
light.lightType = (typeShadow & 0x1u);
#if defined(VARIANT_HAS_SHADOWING)
light.shadowIndex = int((typeShadow >> 8u) & 0xFFu);
light.castsShadows = bool(channels & 0x10000u);
if (light.lightType == LIGHT_TYPE_SPOT) {
light.zLight = dot(shadowUniforms.shadows[light.shadowIndex].lightFromWorldZ, vec4(worldPosition, 1.0));
}
#endif
if (light.lightType == LIGHT_TYPE_SPOT) {
light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset);
}
#endif
return light;
}
void evaluatePunctualLights(const MaterialInputs material,
const PixelParams pixel, inout vec3 color) {
FroxelParams froxel = getFroxelParams(getFroxelIndex(getNormalizedPhysicalViewportCoord()));
uint index = froxel.recordOffset;
uint end = index + froxel.count;
int channels = object_uniforms_flagsChannels & 0xFF;
for ( ; index < end; index++) {
uint lightIndex = getLightIndex(index);
Light light = getLight(lightIndex);
if ((light.channels & channels) == 0) {
continue;
}
#if defined(MATERIAL_CAN_SKIP_LIGHTING)
if (light.NoL <= 0.0 || light.attenuation <= 0.0) {
continue;
}
#endif
float visibility = 1.0;
#if defined(VARIANT_HAS_SHADOWING)
if (light.NoL > 0.0) {
if (light.castsShadows) {
int shadowIndex = light.shadowIndex;
if (light.lightType == LIGHT_TYPE_POINT) {
highp vec3 r = getWorldPosition() - light.worldPosition;
int face = getPointLightFace(r);
shadowIndex += face;
light.zLight = dot(shadowUniforms.shadows[shadowIndex].lightFromWorldZ,
vec4(getWorldPosition(), 1.0));
}
highp vec4 shadowPosition = getShadowPosition(shadowIndex, light.direction, light.zLight);
visibility = shadow(false, sampler0_shadowMap, shadowIndex,
shadowPosition, light.zLight);
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
applyShadowStrength(visibility, material.shadowStrength);
#endif
}
if (light.contactShadows && visibility > 0.0) {
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0) {
visibility *= 1.0 - screenSpaceContactShadow(light.l);
}
}
#if defined(MATERIAL_CAN_SKIP_LIGHTING)
if (visibility <= 0.0) {
continue;
}
#endif
}
#endif
#if defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING)
color.rgb += customSurfaceShading(material, pixel, light, visibility);
#else
color.rgb += surfaceShading(pixel, light, visibility);
#endif
}
if (CONFIG_DEBUG_FROXEL_VISUALIZATION) {
if (froxel.count > 0u && frameUniforms.enableFroxelViz != 0) {
const vec3 debugColors[17] = vec3[](
vec3(0.0, 0.0, 0.0),
vec3(0.0, 0.0, 0.1647),
vec3(0.0, 0.0, 0.3647),
vec3(0.0, 0.0, 0.6647),
vec3(0.0, 0.0, 0.9647),
vec3(0.0, 0.9255, 0.9255),
vec3(0.0, 0.5647, 0.0),
vec3(0.0, 0.7843, 0.0),
vec3(1.0, 1.0, 0.0),
vec3(0.90588, 0.75294, 0.0),
vec3(1.0, 0.5647, 0.0),
vec3(1.0, 0.0, 0.0),
vec3(0.8392, 0.0, 0.0),
vec3(1.0, 0.0, 1.0),
vec3(0.6, 0.3333, 0.7882),
vec3(1.0, 1.0, 1.0),
vec3(1.0, 1.0, 1.0)
);
color = mix(color, debugColors[clamp(froxel.count, 0u, 16u)], 0.8);
}
}
}
void main() {
compute();
}
#if !defined(HAS_CUSTOM_OUTPUT)
layout(location = 0) out vec4 fragColor;
#else
vec4 fragColor;
#endif
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
void blendPostLightingColor(const MaterialInputs material, inout vec4 color) {
vec4 blend = color;
#if defined(POST_LIGHTING_BLEND_MODE_OPAQUE)
blend = material.postLightingColor;
#elif defined(POST_LIGHTING_BLEND_MODE_TRANSPARENT)
blend = material.postLightingColor + color * (1.0 - material.postLightingColor.a);
#elif defined(POST_LIGHTING_BLEND_MODE_ADD)
blend += material.postLightingColor;
#elif defined(POST_LIGHTING_BLEND_MODE_MULTIPLY)
blend *= material.postLightingColor;
#elif defined(POST_LIGHTING_BLEND_MODE_SCREEN)
blend += material.postLightingColor * (1.0 - color);
#endif
color = mix(color, blend, material.postLightingMixFactor);
}
#endif
#if defined(BLEND_MODE_MASKED)
void applyAlphaMask(inout vec4 baseColor) {
baseColor.a = (baseColor.a - getMaskThreshold()) / max(fwidth(baseColor.a), 1e-3) + 0.5;
if (baseColor.a <= getMaskThreshold()) {
discard;
}
}
#else
void applyAlphaMask(inout vec4 baseColor) {}
#endif
void main() {
filament_lodBias = frameUniforms.lodBias;
#if defined(FILAMENT_HAS_FEATURE_INSTANCING)
logical_instance_index = instance_index;
#endif
initObjectUniforms();
computeShadingParams();
MaterialInputs inputs;
initMaterial(inputs);
material(inputs);
applyAlphaMask(inputs.baseColor);
fragColor = evaluateMaterial(inputs);
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR) && !defined(MATERIAL_HAS_REFLECTIONS)
blendPostLightingColor(inputs, fragColor);
#endif
#if defined(VARIANT_HAS_FOG)
highp vec3 view = getWorldPosition() - getWorldCameraPosition();
view = frameUniforms.fogFromWorldMatrix * view;
#if MATERIAL_FEATURE_LEVEL > 0
# if defined (FILAMENT_LINEAR_FOG)
vec4 fogColor = fogLinear(view, sampler0_fog);
# else
vec4 fogColor = fog(view, sampler0_fog);
# endif
#else
vec4 fogColor = fogLinear(view);
#endif
# if defined(BLEND_MODE_OPAQUE)
# elif defined(BLEND_MODE_TRANSPARENT)
fogColor.rgb *= fragColor.a;
# elif defined(BLEND_MODE_ADD)
fogColor.rgb = vec3(0.0);
# elif defined(BLEND_MODE_MASKED)
# elif defined(BLEND_MODE_MULTIPLY)
# elif defined(BLEND_MODE_SCREEN)
#endif
fragColor.rgb = fragColor.rgb * (1.0 - fogColor.a) + fogColor.rgb;
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
if (CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP) {
float a = fragColor.a;
highp vec4 p = getShadowPosition(getShadowCascade());
p.xy = p.xy * (1.0 / p.w);
if (p.xy != saturate(p.xy)) {
vec4 c = vec4(1.0, 0, 1.0, 1.0) * a;
fragColor = mix(fragColor, c, 0.2);
} else {
highp vec2 size = vec2(textureSize(sampler0_shadowMap, 0));
highp int ix = int(floor(p.x * size.x));
highp int iy = int(floor(p.y * size.y));
float t = float((ix ^ iy) & 1) * 0.2;
vec4 c = vec4(vec3(t * a), a);
fragColor = mix(fragColor, c, 0.5);
}
}
#endif
#if MATERIAL_FEATURE_LEVEL == 0
if (CONFIG_SRGB_SWAPCHAIN_EMULATION) {
if (frameUniforms.rec709 != 0) {
fragColor.rgb = pow(fragColor.rgb, vec3(0.45454));
}
}
#endif
#if defined(HAS_CUSTOM_OUTPUT)
FRAG_OUTPUT_AT0 = inputs.FRAG_OUTPUT0;
#endif
}
void main() {
#if defined(FILAMENT_HAS_FEATURE_INSTANCING)
# if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
instance_index = gl_InstanceIndex;
# else
if (CONFIG_POWER_VR_SHADER_WORKAROUNDS) {
instance_index = (1 + gl_InstanceID) - 1;
} else {
instance_index = gl_InstanceID;
}
# endif
logical_instance_index = instance_index;
#endif
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
#if !defined(FILAMENT_HAS_FEATURE_INSTANCING)
#error Instanced stereo not supported at this feature level
#endif
logical_instance_index = instance_index / CONFIG_STEREO_EYE_COUNT;
#endif
initObjectUniforms();
#if defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
#if !defined(VERTEX_DOMAIN_DEVICE) || defined(VARIANT_HAS_VSM)
MaterialVertexInputs material;
initMaterialVertex(material);
materialVertex(material);
#endif
#else
MaterialVertexInputs material;
initMaterialVertex(material);
#if defined(HAS_ATTRIBUTE_TANGENTS)
#if defined(MATERIAL_NEEDS_TBN)
toTangentFrame(mesh_tangents, material.worldNormal, vertex_worldTangent.xyz);
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_MORPHING_TANGENT_BIT) != 0) {
#if defined(LEGACY_MORPHING)
vec3 normal0, normal1, normal2, normal3;
toTangentFrame(mesh_custom4, normal0);
toTangentFrame(mesh_custom5, normal1);
toTangentFrame(mesh_custom6, normal2);
toTangentFrame(mesh_custom7, normal3);
vec3 baseNormal = material.worldNormal;
material.worldNormal += morphingUniforms.weights[0].xyz * (normal0 - baseNormal);
material.worldNormal += morphingUniforms.weights[1].xyz * (normal1 - baseNormal);
material.worldNormal += morphingUniforms.weights[2].xyz * (normal2 - baseNormal);
material.worldNormal += morphingUniforms.weights[3].xyz * (normal3 - baseNormal);
#else
morphNormal(material.worldNormal);
material.worldNormal = normalize(material.worldNormal);
#endif
}
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0) {
skinNormalTangent(material.worldNormal, vertex_worldTangent.xyz, mesh_bone_indices, mesh_bone_weights);
}
#endif
vertex_worldTangent.xyz = getWorldFromModelNormalMatrix() * vertex_worldTangent.xyz;
vertex_worldTangent.w = mesh_tangents.w;
material.worldNormal = getWorldFromModelNormalMatrix() * material.worldNormal;
#else
toTangentFrame(mesh_tangents, material.worldNormal);
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_MORPHING_TANGENT_BIT) != 0) {
#if defined(LEGACY_MORPHING)
vec3 normal0, normal1, normal2, normal3;
toTangentFrame(mesh_custom4, normal0);
toTangentFrame(mesh_custom5, normal1);
toTangentFrame(mesh_custom6, normal2);
toTangentFrame(mesh_custom7, normal3);
vec3 baseNormal = material.worldNormal;
material.worldNormal += morphingUniforms.weights[0].xyz * (normal0 - baseNormal);
material.worldNormal += morphingUniforms.weights[1].xyz * (normal1 - baseNormal);
material.worldNormal += morphingUniforms.weights[2].xyz * (normal2 - baseNormal);
material.worldNormal += morphingUniforms.weights[3].xyz * (normal3 - baseNormal);
#else
morphNormal(material.worldNormal);
material.worldNormal = normalize(material.worldNormal);
#endif
}
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0) {
skinNormal(material.worldNormal, mesh_bone_indices, mesh_bone_weights);
}
#endif
material.worldNormal = getWorldFromModelNormalMatrix() * material.worldNormal;
#endif
#endif
materialVertex(material);
#if defined(HAS_ATTRIBUTE_COLOR)
vertex_color = material.color;
#endif
#if defined(HAS_ATTRIBUTE_UV0)
vertex_uv01.xy = material.uv0;
#endif
#if defined(HAS_ATTRIBUTE_UV1)
vertex_uv01.zw = material.uv1;
#endif
#if defined(VARIABLE_CUSTOM0)
VARIABLE_CUSTOM_AT0 = material.VARIABLE_CUSTOM0;
#endif
#if defined(VARIABLE_CUSTOM1)
VARIABLE_CUSTOM_AT1 = material.VARIABLE_CUSTOM1;
#endif
#if defined(VARIABLE_CUSTOM2)
VARIABLE_CUSTOM_AT2 = material.VARIABLE_CUSTOM2;
#endif
#if defined(VARIABLE_CUSTOM3)
VARIABLE_CUSTOM_AT3 = material.VARIABLE_CUSTOM3;
#endif
#if defined(VARIABLE_CUSTOM4) && !defined(HAS_ATTRIBUTE_COLOR)
VARIABLE_CUSTOM_AT4 = material.VARIABLE_CUSTOM4;
#endif
vertex_worldPosition.xyz = material.worldPosition.xyz;
#ifdef HAS_ATTRIBUTE_TANGENTS
vertex_worldNormal = material.worldNormal;
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
vertex_lightSpacePosition = computeLightSpacePosition(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection,
shadowUniforms.shadows[0].normalBias,
shadowUniforms.shadows[0].lightFromWorldMatrix);
#endif
#endif
vec4 position;
#if defined(VERTEX_DOMAIN_DEVICE)
position = getPosition();
#if !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
#if defined(MATERIAL_HAS_CLIP_SPACE_TRANSFORM)
position = getMaterialClipSpaceTransform(material) * position;
#endif
#endif
#if defined(MATERIAL_HAS_VERTEX_DOMAIN_DEVICE_JITTERED)
position.xy = position.xy * frameUniforms.clipTransform.xy + (position.w * frameUniforms.clipTransform.zw);
#endif
#else
position = getClipFromWorldMatrix() * getWorldPosition(material);
#endif
#if defined(VERTEX_DOMAIN_DEVICE)
position.z = position.z * -0.5 + 0.5;
#endif
#if defined(VARIANT_HAS_VSM)
highp float z = (getViewFromWorldMatrix() * getWorldPosition(material)).z;
highp float depth = -z * frameUniforms.oneOverFarMinusNear - frameUniforms.nearOverFarMinusNear;
depth = depth * 2.0 - 1.0;
vertex_worldPosition.w = depth;
#endif
vertex_position = position;
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
int eyeIndex = instance_index % CONFIG_STEREO_EYE_COUNT;
float ndcViewportWidth = 2.0 / float(CONFIG_STEREO_EYE_COUNT);
float eyeZeroMidpoint = -1.0f + ndcViewportWidth / 2.0;
float transform = eyeZeroMidpoint + ndcViewportWidth * float(eyeIndex);
position.x *= 1.0 / float(CONFIG_STEREO_EYE_COUNT);
position.x += transform * position.w;
float leftClip = position.x +
(1.0 - ndcViewportWidth * float(eyeIndex)) * position.w;
float rightClip = position.x +
(1.0 - ndcViewportWidth * float(eyeIndex + 1)) * position.w;
FILAMENT_CLIPDISTANCE[0] = leftClip;
FILAMENT_CLIPDISTANCE[1] = -rightClip;
#endif
#if defined(TARGET_VULKAN_ENVIRONMENT)
position.y = -position.y;
#endif
#if !defined(TARGET_VULKAN_ENVIRONMENT) && !defined(TARGET_METAL_ENVIRONMENT) && !defined(TARGET_WEBGPU_ENVIRONMENT)
position.z = position.z * frameUniforms.clipControl.x + position.w * frameUniforms.clipControl.y;
#endif
gl_Position = position;
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
instance_index = logical_instance_index;
#endif
}
#if defined(SHADING_MODEL_CLOTH)
#if !defined(MATERIAL_HAS_SUBSURFACE_COLOR)
#define MATERIAL_CAN_SKIP_LIGHTING
#endif
#elif defined(SHADING_MODEL_SUBSURFACE) || defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING)
#else
#define MATERIAL_CAN_SKIP_LIGHTING
#endif
struct MaterialInputs {
vec4 baseColor;
#if !defined(SHADING_MODEL_UNLIT)
#if !defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
float roughness;
#endif
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
float metallic;
float reflectance;
#endif
float ambientOcclusion;
#endif
vec4 emissive;
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT)
vec3 sheenColor;
float sheenRoughness;
#endif
float clearCoat;
float clearCoatRoughness;
float anisotropy;
vec3 anisotropyDirection;
#if defined(SHADING_MODEL_SUBSURFACE) || defined(MATERIAL_HAS_REFRACTION)
float thickness;
#endif
#if defined(SHADING_MODEL_SUBSURFACE)
float subsurfacePower;
vec3 subsurfaceColor;
#endif
#if defined(SHADING_MODEL_CLOTH)
vec3 sheenColor;
#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
vec3 subsurfaceColor;
#endif
#endif
#if defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
vec3 specularColor;
float glossiness;
#endif
#if defined(MATERIAL_HAS_NORMAL)
vec3 normal;
#endif
#if defined(MATERIAL_HAS_BENT_NORMAL)
vec3 bentNormal;
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT) && defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
vec3 clearCoatNormal;
#endif
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
vec4 postLightingColor;
float postLightingMixFactor;
#endif
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT)
#if defined(MATERIAL_HAS_REFRACTION)
#if defined(MATERIAL_HAS_ABSORPTION)
vec3 absorption;
#endif
#if defined(MATERIAL_HAS_TRANSMISSION)
float transmission;
#endif
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
float dispersion;
#endif
#if defined(MATERIAL_HAS_IOR)
float ior;
#endif
#if defined(MATERIAL_HAS_MICRO_THICKNESS) && (REFRACTION_TYPE == REFRACTION_TYPE_THIN)
float microThickness;
#endif
#elif !defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
#if defined(MATERIAL_HAS_IOR)
float ior;
#endif
#endif
#endif
#if defined(MATERIAL_HAS_SPECULAR_FACTOR)
float specularFactor;
#endif
#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR)
vec3 specularColorFactor;
#endif
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
float shadowStrength;
#endif
#if defined(FRAG_OUTPUT0)
FRAG_OUTPUT_PRECISION0 FRAG_OUTPUT_MATERIAL_TYPE0 FRAG_OUTPUT0;
#endif
};
void initMaterial(out MaterialInputs material) {
material.baseColor = vec4(1.0);
#if !defined(SHADING_MODEL_UNLIT)
#if !defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
material.roughness = 1.0;
#endif
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
material.metallic = 0.0;
material.reflectance = 0.5;
#endif
material.ambientOcclusion = 1.0;
#endif
material.emissive = vec4(vec3(0.0), 1.0);
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT)
#if defined(MATERIAL_HAS_SHEEN_COLOR)
material.sheenColor = vec3(0.0);
material.sheenRoughness = 0.0;
#endif
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT)
material.clearCoat = 1.0;
material.clearCoatRoughness = 0.0;
#endif
#if defined(MATERIAL_HAS_ANISOTROPY)
material.anisotropy = 0.0;
material.anisotropyDirection = vec3(1.0, 0.0, 0.0);
#endif
#if defined(SHADING_MODEL_SUBSURFACE) || defined(MATERIAL_HAS_REFRACTION)
material.thickness = 0.5;
#endif
#if defined(SHADING_MODEL_SUBSURFACE)
material.subsurfacePower = 12.234;
material.subsurfaceColor = vec3(1.0);
#endif
#if defined(SHADING_MODEL_CLOTH)
material.sheenColor = sqrt(material.baseColor.rgb);
#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
material.subsurfaceColor = vec3(0.0);
#endif
#endif
#if defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
material.glossiness = 0.0;
material.specularColor = vec3(0.0);
#endif
#if defined(MATERIAL_HAS_NORMAL)
material.normal = vec3(0.0, 0.0, 1.0);
#endif
#if defined(MATERIAL_HAS_BENT_NORMAL)
material.bentNormal = vec3(0.0, 0.0, 1.0);
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT) && defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
material.clearCoatNormal = vec3(0.0, 0.0, 1.0);
#endif
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
material.postLightingColor = vec4(0.0);
material.postLightingMixFactor = 1.0;
#endif
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT)
#if defined(MATERIAL_HAS_REFRACTION)
#if defined(MATERIAL_HAS_ABSORPTION)
material.absorption = vec3(0.0);
#endif
#if defined(MATERIAL_HAS_TRANSMISSION)
material.transmission = 1.0;
#endif
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
material.dispersion = 0.0f;
#endif
#if defined(MATERIAL_HAS_IOR)
material.ior = 1.5;
#endif
#if defined(MATERIAL_HAS_MICRO_THICKNESS) && (REFRACTION_TYPE == REFRACTION_TYPE_THIN)
material.microThickness = 0.0;
#endif
#elif !defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
#if defined(MATERIAL_HAS_IOR)
material.ior = 1.5;
#endif
#endif
#endif
#if defined(MATERIAL_HAS_SPECULAR_FACTOR)
material.specularFactor = 1.0;
#endif
#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR)
material.specularColorFactor = vec3(1.0);
#endif
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
material.shadowStrength = 0.0;
#endif
#if defined(FRAG_OUTPUT0)
material.FRAG_OUTPUT0 = FRAG_OUTPUT_MATERIAL_TYPE0(0.0);
#endif
}
#if defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING)
struct LightData {
vec4 colorIntensity;
vec3 l;
float NdotL;
vec3 worldPosition;
float attenuation;
float visibility;
};
struct ShadingData {
vec3 diffuseColor;
float perceptualRoughness;
vec3 f0;
float roughness;
};
#endif
struct MaterialVertexInputs {
#ifdef HAS_ATTRIBUTE_COLOR
vec4 color;
#endif
#ifdef HAS_ATTRIBUTE_UV0
vec2 uv0;
#endif
#ifdef HAS_ATTRIBUTE_UV1
vec2 uv1;
#endif
#ifdef VARIABLE_CUSTOM0
vec4 VARIABLE_CUSTOM0;
#endif
#ifdef VARIABLE_CUSTOM1
vec4 VARIABLE_CUSTOM1;
#endif
#ifdef VARIABLE_CUSTOM2
vec4 VARIABLE_CUSTOM2;
#endif
#ifdef VARIABLE_CUSTOM3
vec4 VARIABLE_CUSTOM3;
#endif
#if defined(VARIABLE_CUSTOM4) && !defined(HAS_ATTRIBUTE_COLOR)
vec4 VARIABLE_CUSTOM4;
#endif
#ifdef HAS_ATTRIBUTE_TANGENTS
vec3 worldNormal;
#endif
vec4 worldPosition;
#ifdef VERTEX_DOMAIN_DEVICE
#ifdef MATERIAL_HAS_CLIP_SPACE_TRANSFORM
mat4 clipSpaceTransform;
#endif
#endif
};
vec4 getWorldPosition(const MaterialVertexInputs material) {
return material.worldPosition;
}
#ifdef VERTEX_DOMAIN_DEVICE
#ifdef MATERIAL_HAS_CLIP_SPACE_TRANSFORM
mat4 getMaterialClipSpaceTransform(const MaterialVertexInputs material) {
return material.clipSpaceTransform;
}
#endif
#endif
void initMaterialVertex(out MaterialVertexInputs material) {
#ifdef HAS_ATTRIBUTE_COLOR
material.color = mesh_color;
#endif
#ifdef HAS_ATTRIBUTE_UV0
#ifdef FLIP_UV_ATTRIBUTE
material.uv0 = vec2(mesh_uv0.x, 1.0 - mesh_uv0.y);
#else
material.uv0 = mesh_uv0;
#endif
#endif
#ifdef HAS_ATTRIBUTE_UV1
#ifdef FLIP_UV_ATTRIBUTE
material.uv1 = vec2(mesh_uv1.x, 1.0 - mesh_uv1.y);
#else
material.uv1 = mesh_uv1;
#endif
#endif
#ifdef VARIABLE_CUSTOM0
material.VARIABLE_CUSTOM0 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM1
material.VARIABLE_CUSTOM1 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM2
material.VARIABLE_CUSTOM2 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM3
material.VARIABLE_CUSTOM3 = vec4(0.0);
#endif
#if defined(VARIABLE_CUSTOM4) && !defined(HAS_ATTRIBUTE_COLOR)
material.VARIABLE_CUSTOM4 = vec4(0.0);
#endif
material.worldPosition = computeWorldPosition();
#ifdef VERTEX_DOMAIN_DEVICE
#ifdef MATERIAL_HAS_CLIP_SPACE_TRANSFORM
material.clipSpaceTransform = mat4(1.0);
#endif
#endif
}
void main() {
PostProcessInputs inputs;
postProcess(inputs);
#if MATERIAL_FEATURE_LEVEL == 0
if (CONFIG_SRGB_SWAPCHAIN_EMULATION) {
if (frameUniforms.rec709 != 0) {
inputs.FRAG_OUTPUT0.rgb = pow(inputs.FRAG_OUTPUT0.rgb, vec3(0.45454));
}
}
#endif
#if defined(FRAG_OUTPUT0)
FRAG_OUTPUT_AT0 FRAG_OUTPUT_SWIZZLE0 = inputs.FRAG_OUTPUT0;
#endif
#if defined(FRAG_OUTPUT1)
FRAG_OUTPUT_AT1 FRAG_OUTPUT_SWIZZLE1 = inputs.FRAG_OUTPUT1;
#endif
#if defined(FRAG_OUTPUT2)
FRAG_OUTPUT_AT2 FRAG_OUTPUT_SWIZZLE2 = inputs.FRAG_OUTPUT2;
#endif
#if defined(FRAG_OUTPUT3)
FRAG_OUTPUT_AT3 FRAG_OUTPUT_SWIZZLE3 = inputs.FRAG_OUTPUT3;
#endif
#if defined(FRAG_OUTPUT4)
FRAG_OUTPUT_AT4 FRAG_OUTPUT_SWIZZLE4 = inputs.FRAG_OUTPUT4;
#endif
#if defined(FRAG_OUTPUT5)
FRAG_OUTPUT_AT5 FRAG_OUTPUT_SWIZZLE5 = inputs.FRAG_OUTPUT5;
#endif
#if defined(FRAG_OUTPUT6)
FRAG_OUTPUT_AT6 FRAG_OUTPUT_SWIZZLE6 = inputs.FRAG_OUTPUT6;
#endif
#if defined(FRAG_OUTPUT7)
FRAG_OUTPUT_AT7 FRAG_OUTPUT_SWIZZLE7 = inputs.FRAG_OUTPUT7;
#endif
#if defined(FRAG_OUTPUT_DEPTH)
gl_FragDepth = inputs.depth;
#endif
}
void main() {
PostProcessVertexInputs inputs;
initPostProcessMaterialVertex(inputs);
inputs.normalizedUV = position.xy * 0.5 + 0.5;
inputs.position = getPosition();
postProcessVertex(inputs);
inputs.position.z = inputs.position.z * -0.5 + 0.5;
#if defined(TARGET_VULKAN_ENVIRONMENT)
inputs.position.y = -inputs.position.y;
#endif
#if !defined(TARGET_VULKAN_ENVIRONMENT) && !defined(TARGET_METAL_ENVIRONMENT) && !defined(TARGET_WEBGPU_ENVIRONMENT)
inputs.position.z = inputs.position.z * frameUniforms.clipControl.x + inputs.position.w * frameUniforms.clipControl.y;
#endif
#if defined(VARIABLE_CUSTOM0)
VARIABLE_CUSTOM_AT0 = inputs.VARIABLE_CUSTOM0;
#endif
#if defined(VARIABLE_CUSTOM1)
VARIABLE_CUSTOM_AT1 = inputs.VARIABLE_CUSTOM1;
#endif
#if defined(VARIABLE_CUSTOM2)
VARIABLE_CUSTOM_AT2 = inputs.VARIABLE_CUSTOM2;
#endif
#if defined(VARIABLE_CUSTOM3)
VARIABLE_CUSTOM_AT3 = inputs.VARIABLE_CUSTOM3;
#endif
#if defined(VARIABLE_CUSTOM4)
VARIABLE_CUSTOM_AT4 = inputs.VARIABLE_CUSTOM4;
#endif
gl_Position = inputs.position;
}
vec4 getPosition() {
return position;
}
struct PostProcessInputs {
#if defined(FRAG_OUTPUT0)
FRAG_OUTPUT_PRECISION0 FRAG_OUTPUT_MATERIAL_TYPE0 FRAG_OUTPUT0;
#endif
#if defined(FRAG_OUTPUT1)
FRAG_OUTPUT_PRECISION1 FRAG_OUTPUT_MATERIAL_TYPE1 FRAG_OUTPUT1;
#endif
#if defined(FRAG_OUTPUT2)
FRAG_OUTPUT_PRECISION2 FRAG_OUTPUT_MATERIAL_TYPE2 FRAG_OUTPUT2;
#endif
#if defined(FRAG_OUTPUT3)
FRAG_OUTPUT_PRECISION3 FRAG_OUTPUT_MATERIAL_TYPE3 FRAG_OUTPUT3;
#endif
#if defined(FRAG_OUTPUT4)
FRAG_OUTPUT_PRECISION4 FRAG_OUTPUT_MATERIAL_TYPE4 FRAG_OUTPUT4;
#endif
#if defined(FRAG_OUTPUT5)
FRAG_OUTPUT_PRECISION5 FRAG_OUTPUT_MATERIAL_TYPE5 FRAG_OUTPUT5;
#endif
#if defined(FRAG_OUTPUT6)
FRAG_OUTPUT_PRECISION6 FRAG_OUTPUT_MATERIAL_TYPE6 FRAG_OUTPUT6;
#endif
#if defined(FRAG_OUTPUT7)
FRAG_OUTPUT_PRECISION7 FRAG_OUTPUT_MATERIAL_TYPE7 FRAG_OUTPUT7;
#endif
#if defined(FRAG_OUTPUT_DEPTH)
float depth;
#endif
};
LAYOUT_LOCATION(LOCATION_POSITION) ATTRIBUTE vec4 position;
struct PostProcessVertexInputs {
vec2 normalizedUV;
vec4 position;
#ifdef VARIABLE_CUSTOM0
vec4 VARIABLE_CUSTOM0;
#endif
#ifdef VARIABLE_CUSTOM1
vec4 VARIABLE_CUSTOM1;
#endif
#ifdef VARIABLE_CUSTOM2
vec4 VARIABLE_CUSTOM2;
#endif
#ifdef VARIABLE_CUSTOM3
vec4 VARIABLE_CUSTOM3;
#endif
#ifdef VARIABLE_CUSTOM4
vec4 VARIABLE_CUSTOM4;
#endif
};
void initPostProcessMaterialVertex(out PostProcessVertexInputs inputs) {
#ifdef VARIABLE_CUSTOM0
inputs.VARIABLE_CUSTOM0 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM1
inputs.VARIABLE_CUSTOM1 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM2
inputs.VARIABLE_CUSTOM2 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM3
inputs.VARIABLE_CUSTOM3 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM4
inputs.VARIABLE_CUSTOM4 = vec4(0.0);
#endif
}
#if defined(BLEND_MODE_MASKED)
float computeDiffuseAlpha(float a) {
return (frameUniforms.needsAlphaChannel == 1.0) ? 1.0 : a;
}
#else
float computeDiffuseAlpha(float a) {
#if defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)
return a;
#else
return 1.0;
#endif
}
#endif
#if defined(GEOMETRIC_SPECULAR_AA)
float normalFiltering(float perceptualRoughness, const vec3 worldNormal) {
vec3 du = dFdx(worldNormal);
vec3 dv = dFdy(worldNormal);
du *= frameUniforms.derivativesScale.x;
dv *= frameUniforms.derivativesScale.y;
float variance = materialParams._specularAntiAliasingVariance * (dot(du, du) + dot(dv, dv));
float roughness = perceptualRoughnessToRoughness(perceptualRoughness);
float kernelRoughness = min(2.0 * variance, materialParams._specularAntiAliasingThreshold);
float squareRoughness = saturate(roughness * roughness + kernelRoughness);
return roughnessToPerceptualRoughness(sqrt(squareRoughness));
}
#endif
void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel) {
vec4 baseColor = material.baseColor;
#if defined(BLEND_MODE_FADE) && !defined(SHADING_MODEL_UNLIT)
unpremultiply(baseColor);
#endif
#if defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
vec3 specularColor = material.specularColor;
float metallic = computeMetallicFromSpecularColor(specularColor);
pixel.diffuseColor = computeDiffuseColor(baseColor, metallic);
pixel.f0 = specularColor;
#elif !defined(SHADING_MODEL_CLOTH)
pixel.diffuseColor = computeDiffuseColor(baseColor, material.metallic);
#if !defined(SHADING_MODEL_SUBSURFACE) && (!defined(MATERIAL_HAS_REFLECTANCE) && defined(MATERIAL_HAS_IOR))
float reflectance = iorToF0(max(1.0, material.ior), 1.0);
#else
float reflectance = computeDielectricF0(material.reflectance);
#endif
#if !defined(MATERIAL_HAS_SPECULAR_FACTOR) && !defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR)
pixel.f0 = computeF0(baseColor, material.metallic, reflectance);
#else
vec3 dielectricSpecularF0 = vec3(0.0);
float dielectricSpecularF90 = 0.0;
#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR)
dielectricSpecularF0 = min(reflectance * material.specularColorFactor, vec3(1.0));
#endif
#if defined(MATERIAL_HAS_SPECULAR_FACTOR)
dielectricSpecularF0 *= material.specularFactor;
dielectricSpecularF90 = material.specularFactor;
#endif
pixel.f0 = baseColor.rgb * material.metallic + dielectricSpecularF0 * (1.0 - material.metallic);
pixel.f90 = material.metallic + dielectricSpecularF90 * (1.0 - material.metallic);
#endif
#else
pixel.diffuseColor = baseColor.rgb;
pixel.f0 = material.sheenColor;
#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
pixel.subsurfaceColor = material.subsurfaceColor;
#endif
#endif
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE)
#if defined(MATERIAL_HAS_REFRACTION)
const float airIor = 1.0;
#if !defined(MATERIAL_HAS_IOR)
float materialIor = f0ToIor(pixel.f0.g);
#else
float materialIor = max(1.0, material.ior);
#endif
pixel.etaIR = airIor / materialIor;
pixel.etaRI = materialIor / airIor;
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
pixel.dispersion = material.dispersion;
#endif
#if defined(MATERIAL_HAS_TRANSMISSION)
pixel.transmission = saturate(material.transmission);
#else
pixel.transmission = 1.0;
#endif
#if defined(MATERIAL_HAS_ABSORPTION)
pixel.absorption = max(vec3(0.0), material.absorption);
#else
pixel.absorption = vec3(0.0);
#endif
#if defined(SHADING_MODEL_SUBSURFACE) || defined(MATERIAL_HAS_REFRACTION)
pixel.thickness = max(0.0, material.thickness);
#endif
#if defined(MATERIAL_HAS_MICRO_THICKNESS) && (REFRACTION_TYPE == REFRACTION_TYPE_THIN)
pixel.uThickness = max(0.0, material.microThickness);
#else
pixel.uThickness = 0.0;
#endif
#endif
#endif
}
void getSpecularPixelParams(const MaterialInputs material, inout PixelParams pixel) {
#if defined(MATERIAL_HAS_SPECULAR_FACTOR)
pixel.specular = material.specularFactor;
#endif
#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR)
pixel.specularColor = material.specularColorFactor;
#endif
}
void getSheenPixelParams(const MaterialInputs material, inout PixelParams pixel) {
#if defined(MATERIAL_HAS_SHEEN_COLOR) && !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE)
pixel.sheenColor = material.sheenColor;
float sheenPerceptualRoughness = material.sheenRoughness;
sheenPerceptualRoughness = clamp(sheenPerceptualRoughness, MIN_PERCEPTUAL_ROUGHNESS, 1.0);
#if defined(GEOMETRIC_SPECULAR_AA)
sheenPerceptualRoughness =
normalFiltering(sheenPerceptualRoughness, getWorldGeometricNormalVector());
#endif
pixel.sheenPerceptualRoughness = sheenPerceptualRoughness;
pixel.sheenRoughness = perceptualRoughnessToRoughness(sheenPerceptualRoughness);
#endif
}
void getClearCoatPixelParams(const MaterialInputs material, inout PixelParams pixel) {
#if defined(MATERIAL_HAS_CLEAR_COAT)
pixel.clearCoat = material.clearCoat;
float clearCoatPerceptualRoughness = material.clearCoatRoughness;
clearCoatPerceptualRoughness =
clamp(clearCoatPerceptualRoughness, MIN_PERCEPTUAL_ROUGHNESS, 1.0);
#if defined(GEOMETRIC_SPECULAR_AA)
clearCoatPerceptualRoughness =
normalFiltering(clearCoatPerceptualRoughness, getWorldGeometricNormalVector());
#endif
pixel.clearCoatPerceptualRoughness = clearCoatPerceptualRoughness;
pixel.clearCoatRoughness = perceptualRoughnessToRoughness(clearCoatPerceptualRoughness);
#if defined(CLEAR_COAT_IOR_CHANGE)
pixel.f0 = mix(pixel.f0, f0ClearCoatToSurface(pixel.f0), pixel.clearCoat);
#endif
#endif
}
void getRoughnessPixelParams(const MaterialInputs material, inout PixelParams pixel) {
#if defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
float perceptualRoughness = computeRoughnessFromGlossiness(material.glossiness);
#else
float perceptualRoughness = material.roughness;
#endif
pixel.perceptualRoughnessUnclamped = perceptualRoughness;
#if defined(GEOMETRIC_SPECULAR_AA)
perceptualRoughness = normalFiltering(perceptualRoughness, getWorldGeometricNormalVector());
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT) && defined(MATERIAL_HAS_CLEAR_COAT_ROUGHNESS)
float basePerceptualRoughness = max(perceptualRoughness, pixel.clearCoatPerceptualRoughness);
perceptualRoughness = mix(perceptualRoughness, basePerceptualRoughness, pixel.clearCoat);
#endif
pixel.perceptualRoughness = clamp(perceptualRoughness, MIN_PERCEPTUAL_ROUGHNESS, 1.0);
pixel.roughness = perceptualRoughnessToRoughness(pixel.perceptualRoughness);
}
void getSubsurfacePixelParams(const MaterialInputs material, inout PixelParams pixel) {
#if defined(SHADING_MODEL_SUBSURFACE)
pixel.subsurfacePower = material.subsurfacePower;
pixel.subsurfaceColor = material.subsurfaceColor;
pixel.thickness = saturate(material.thickness);
#endif
}
void getEnergyCompensationPixelParams(inout PixelParams pixel) {
pixel.dfg = prefilteredDFG(pixel.perceptualRoughness, shading_NoV);
#if !defined(SHADING_MODEL_CLOTH)
pixel.energyCompensation = 1.0 + pixel.f0 * (1.0 / pixel.dfg.y - 1.0);
#else
pixel.energyCompensation = vec3(1.0);
#endif
#if !defined(SHADING_MODEL_CLOTH)
#if defined(MATERIAL_HAS_SHEEN_COLOR)
pixel.sheenDFG = prefilteredDFG(pixel.sheenPerceptualRoughness, shading_NoV).z;
pixel.sheenScaling = 1.0 - max3(pixel.sheenColor) * pixel.sheenDFG;
#endif
#endif
}
void getPixelParams(const MaterialInputs material, out PixelParams pixel) {
getSpecularPixelParams(material, pixel);
getCommonPixelParams(material, pixel);
getSheenPixelParams(material, pixel);
getClearCoatPixelParams(material, pixel);
getRoughnessPixelParams(material, pixel);
getSubsurfacePixelParams(material, pixel);
getAnisotropyPixelParams(material, pixel);
getEnergyCompensationPixelParams(pixel);
}
vec4 evaluateLights(const MaterialInputs material) {
PixelParams pixel;
getPixelParams(material, pixel);
vec3 color = vec3(0.0);
evaluateIBL(material, pixel, color);
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
evaluateDirectionalLight(material, pixel, color);
#endif
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
evaluatePunctualLights(material, pixel, color);
#endif
#if defined(BLEND_MODE_FADE) && !defined(SHADING_MODEL_UNLIT)
color *= material.baseColor.a;
#endif
return vec4(color, computeDiffuseAlpha(material.baseColor.a));
}
vec3 emissive(const MaterialInputs material, float alpha) {
#if defined(MATERIAL_HAS_EMISSIVE)
highp vec4 emissive = material.emissive;
highp float attenuation = mix(1.0, getExposure(), emissive.w);
#if defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)
attenuation *= alpha;
#endif
return emissive.rgb * attenuation;
#else
return vec3(0.0);
#endif
}
vec4 evaluateMaterial(const MaterialInputs material) {
vec4 color = evaluateLights(material);
color.rgb += emissive(material, color.a);
color.rgb = min(color.rgb, MEDIUMP_FLT_MAX);
return color;
}
vec3 customSurfaceShading(const MaterialInputs materialInputs,
const PixelParams pixel, const Light light, float visibility) {
LightData lightData;
lightData.colorIntensity = light.colorIntensity;
lightData.l = light.l;
lightData.NdotL = light.NoL;
lightData.worldPosition = light.worldPosition;
lightData.attenuation = light.attenuation;
lightData.visibility = visibility;
ShadingData shadingData;
shadingData.diffuseColor = pixel.diffuseColor;
shadingData.perceptualRoughness = pixel.perceptualRoughness;
shadingData.f0 = pixel.f0;
shadingData.roughness = pixel.roughness;
return surfaceShading(materialInputs, shadingData, lightData);
}
vec3 surfaceShading(const PixelParams pixel, const Light light, float occlusion) {
vec3 h = normalize(shading_view + light.l);
float NoL = light.NoL;
float NoH = saturate(dot(shading_normal, h));
float LoH = saturate(dot(light.l, h));
float D = distributionCloth(pixel.roughness, NoH);
float V = visibilityCloth(shading_NoV, NoL);
vec3 F = pixel.f0;
vec3 Fr = (D * V) * F;
float diffuse = diffuse(pixel.roughness, shading_NoV, NoL, LoH);
#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
diffuse *= Fd_Wrap(dot(shading_normal, light.l), 0.5);
#endif
vec3 Fd = diffuse * pixel.diffuseColor;
#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
Fd *= saturate(pixel.subsurfaceColor + NoL);
vec3 color = Fd + Fr * NoL;
color *= light.colorIntensity.rgb * (light.colorIntensity.w * light.attenuation * occlusion);
#else
vec3 color = Fd + Fr;
color *= light.colorIntensity.rgb * (light.colorIntensity.w * light.attenuation * NoL * occlusion);
#endif
return color;
}
#if defined(MATERIAL_HAS_SHEEN_COLOR)
vec3 sheenLobe(const PixelParams pixel, float NoV, float NoL, float NoH) {
float D = distributionCloth(pixel.sheenRoughness, NoH);
float V = visibilityCloth(NoV, NoL);
return (D * V) * pixel.sheenColor;
}
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT)
float clearCoatLobe(const PixelParams pixel, const vec3 h, float NoH, float LoH, out float Fcc) {
#if defined(MATERIAL_HAS_NORMAL) || defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
float clearCoatNoH = saturate(dot(shading_clearCoatNormal, h));
#else
float clearCoatNoH = NoH;
#endif
float D = distributionClearCoat(pixel.clearCoatRoughness, clearCoatNoH, h);
float V = visibilityClearCoat(LoH);
float F = F_Schlick(0.04, 1.0, LoH) * pixel.clearCoat;
Fcc = F;
return D * V * F;
}
#endif
#if defined(MATERIAL_HAS_ANISOTROPY)
vec3 anisotropicLobe(const PixelParams pixel, const Light light, const vec3 h,
float NoV, float NoL, float NoH, float LoH) {
vec3 l = light.l;
vec3 t = pixel.anisotropicT;
vec3 b = pixel.anisotropicB;
vec3 v = shading_view;
float ToV = dot(t, v);
float BoV = dot(b, v);
float ToL = dot(t, l);
float BoL = dot(b, l);
float ToH = dot(t, h);
float BoH = dot(b, h);
float at = max(pixel.roughness * (1.0 + pixel.anisotropy), MIN_ROUGHNESS);
float ab = max(pixel.roughness * (1.0 - pixel.anisotropy), MIN_ROUGHNESS);
float D = distributionAnisotropic(at, ab, ToH, BoH, NoH);
float V = visibilityAnisotropic(pixel.roughness, at, ab, ToV, BoV, ToL, BoL, NoV, NoL);
vec3 F = fresnel(pixel.f0, LoH);
return (D * V) * F;
}
#endif
vec3 isotropicLobe(const PixelParams pixel, const Light light, const vec3 h,
float NoV, float NoL, float NoH, float LoH) {
float D = distribution(pixel.roughness, NoH, h);
float V = visibility(pixel.roughness, NoV, NoL);
#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR)
vec3 F = fresnel(pixel.f0, pixel.f90, LoH);
#else
vec3 F = fresnel(pixel.f0, LoH);
#endif
return (D * V) * F;
}
vec3 specularLobe(const PixelParams pixel, const Light light, const vec3 h,
float NoV, float NoL, float NoH, float LoH) {
#if defined(MATERIAL_HAS_ANISOTROPY)
return anisotropicLobe(pixel, light, h, NoV, NoL, NoH, LoH);
#else
return isotropicLobe(pixel, light, h, NoV, NoL, NoH, LoH);
#endif
}
vec3 diffuseLobe(const PixelParams pixel, float NoV, float NoL, float LoH) {
return pixel.diffuseColor * diffuse(pixel.roughness, NoV, NoL, LoH);
}
vec3 surfaceShading(const PixelParams pixel, const Light light, float occlusion) {
vec3 h = normalize(shading_view + light.l);
float NoV = shading_NoV;
float NoL = saturate(light.NoL);
float NoH = saturate(dot(shading_normal, h));
float LoH = saturate(dot(light.l, h));
vec3 Fr = specularLobe(pixel, light, h, NoV, NoL, NoH, LoH);
vec3 Fd = diffuseLobe(pixel, NoV, NoL, LoH);
#if defined(MATERIAL_HAS_REFRACTION)
Fd *= (1.0 - pixel.transmission);
#endif
vec3 color = Fd + Fr * pixel.energyCompensation;
#if defined(MATERIAL_HAS_SHEEN_COLOR)
color *= pixel.sheenScaling;
color += sheenLobe(pixel, NoV, NoL, NoH);
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT)
float Fcc;
float clearCoat = clearCoatLobe(pixel, h, NoH, LoH, Fcc);
float attenuation = 1.0 - Fcc;
#if defined(MATERIAL_HAS_NORMAL) || defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
color *= attenuation * NoL;
float clearCoatNoL = saturate(dot(shading_clearCoatNormal, light.l));
color += clearCoat * clearCoatNoL;
return (color * light.colorIntensity.rgb) *
(light.colorIntensity.w * light.attenuation * occlusion);
#else
color *= attenuation;
color += clearCoat;
#endif
#endif
return (color * light.colorIntensity.rgb) *
(light.colorIntensity.w * light.attenuation * NoL * occlusion);
}
vec3 surfaceShading(const PixelParams pixel, const Light light, float occlusion) {
vec3 h = normalize(shading_view + light.l);
float NoL = light.NoL;
float NoH = saturate(dot(shading_normal, h));
float LoH = saturate(dot(light.l, h));
vec3 Fr = vec3(0.0);
if (NoL > 0.0) {
float D = distribution(pixel.roughness, NoH, h);
float V = visibility(pixel.roughness, shading_NoV, NoL);
vec3 F = fresnel(pixel.f0, LoH);
Fr = (D * V) * F * pixel.energyCompensation;
}
vec3 Fd = pixel.diffuseColor * diffuse(pixel.roughness, shading_NoV, NoL, LoH);
vec3 color = (Fd + Fr) * (NoL * occlusion);
float scatterVoH = saturate(dot(shading_view, -light.l));
float forwardScatter = exp2(scatterVoH * pixel.subsurfacePower - pixel.subsurfacePower);
float backScatter = saturate(NoL * pixel.thickness + (1.0 - pixel.thickness)) * 0.5;
float subsurface = mix(backScatter, 1.0, forwardScatter) * (1.0 - pixel.thickness);
color += pixel.subsurfaceColor * (subsurface * Fd_Lambert());
return (color * light.colorIntensity.rgb) * (light.colorIntensity.w * light.attenuation);
}
void computeShadingParams() {
#if defined(HAS_ATTRIBUTE_TANGENTS)
highp vec3 n = vertex_worldNormal;
#if defined(MATERIAL_NEEDS_TBN)
highp vec3 t = vertex_worldTangent.xyz;
highp vec3 b = cross(n, t) * sign(vertex_worldTangent.w);
#endif
#if defined(MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY)
if (isDoubleSided()) {
n = gl_FrontFacing ? n : -n;
#if defined(MATERIAL_NEEDS_TBN)
t = gl_FrontFacing ? t : -t;
b = gl_FrontFacing ? b : -b;
#endif
}
#endif
shading_geometricNormal = normalize(n);
#if defined(MATERIAL_NEEDS_TBN)
shading_tangentToWorld = mat3(t, b, n);
#endif
#endif
shading_position = vertex_worldPosition.xyz;
highp vec3 sv = isPerspectiveProjection() ?
(frameUniforms.worldFromViewMatrix[3].xyz - shading_position) :
frameUniforms.worldFromViewMatrix[2].xyz;
shading_view = normalize(sv);
shading_normalizedViewportCoord = vertex_position.xy * (0.5 / vertex_position.w) + 0.5;
}
void prepareMaterial(const MaterialInputs material) {
#if defined(HAS_ATTRIBUTE_TANGENTS)
#if defined(MATERIAL_HAS_NORMAL)
shading_normal = normalize(shading_tangentToWorld * material.normal);
#else
shading_normal = getWorldGeometricNormalVector();
#endif
shading_NoV = clampNoV(dot(shading_normal, shading_view));
shading_reflected = reflect(-shading_view, shading_normal);
#if defined(MATERIAL_HAS_BENT_NORMAL)
shading_bentNormal = normalize(shading_tangentToWorld * material.bentNormal);
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT)
#if defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
shading_clearCoatNormal = normalize(shading_tangentToWorld * material.clearCoatNormal);
#else
shading_clearCoatNormal = getWorldGeometricNormalVector();
#endif
#endif
#endif
}
vec4 evaluateMaterial(const MaterialInputs material) {
#if defined(MATERIAL_HAS_REFLECTIONS)
PixelParams pixel;
getAnisotropyPixelParams(material, pixel);
vec4 color = vec4(0.0);
if (frameUniforms.ssrDistance > 0.0) {
vec3 r = getReflectedVector(pixel, shading_view, shading_normal);
color = evaluateScreenSpaceReflections(r);
}
#else
const vec4 color = vec4(0.0);
#endif
return color;
}
void addEmissive(const MaterialInputs material, inout vec4 color) {
#if defined(MATERIAL_HAS_EMISSIVE)
highp vec4 emissive = material.emissive;
highp float attenuation = mix(1.0, getExposure(), emissive.w);
#if defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)
attenuation *= color.a;
#endif
color.rgb += emissive.rgb * attenuation;
#endif
}
vec4 fixupAlpha(vec4 color) {
#if defined(BLEND_MODE_MASKED)
return vec4(color.rgb, (frameUniforms.needsAlphaChannel == 1.0) ? 1.0 : color.a);
#else
return color;
#endif
}
vec4 evaluateMaterial(const MaterialInputs material) {
vec4 color = material.baseColor;
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
#if defined(VARIANT_HAS_SHADOWING)
float visibility = 1.0;
int cascade = getShadowCascade();
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
highp vec4 shadowPosition = getShadowPosition(cascade);
visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0);
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
applyShadowStrength(visibility, material.shadowStrength);
#endif
#if defined (FILAMENT_SHADOW_FAR_ATTENUATION)
highp vec3 v = getWorldPosition() - getWorldCameraPosition();
highp float z = dot(transpose(getViewFromWorldMatrix())[2].xyz, v);
highp vec2 p = frameUniforms.shadowFarAttenuationParams;
visibility = 1.0 - ((1.0 - visibility) * saturate(p.x - z * z * p.y));
#endif
}
if ((frameUniforms.directionalShadows & 0x2) != 0 && visibility > 0.0) {
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0) {
visibility *= (1.0 - screenSpaceContactShadow(frameUniforms.lightDirection));
}
}
color *= 1.0 - visibility;
#else
color = vec4(0.0);
#endif
#elif defined(MATERIAL_HAS_SHADOW_MULTIPLIER)
color = vec4(0.0);
#endif
addEmissive(material, color);
return fixupAlpha(color);
}
#define SHADOW_SAMPLING_RUNTIME_PCF 0u
#define SHADOW_SAMPLING_RUNTIME_EVSM 1u
#define SHADOW_SAMPLING_RUNTIME_DPCF 2u
#define SHADOW_SAMPLING_RUNTIME_PCSS 3u
#define SHADOW_SAMPLING_PCF_HARD 0
#define SHADOW_SAMPLING_PCF_LOW 1
float ShadowSample_PCF_Hard(const mediump sampler2DArrayShadow map,
const highp vec4 scissorNormalized,
const uint layer, const highp vec4 shadowPosition) {
highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w);
position.xy = clamp(position.xy, scissorNormalized.xy, scissorNormalized.zw);
position.z = saturate(position.z);
return texture(map, vec4(position.xy, layer, position.z));
}
float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map,
const highp vec4 scissorNormalized,
const uint layer, const highp vec4 shadowPosition) {
highp vec2 size = vec2(frameUniforms.shadowAtlasResolution.x);
highp vec2 texelSize = vec2(frameUniforms.shadowAtlasResolution.y);
highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w);
position.z = saturate(position.z);
position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0));
vec2 offset = vec2(0.5);
highp vec2 uv = (position.xy * size) + offset;
highp vec2 base = (floor(uv) - offset) * texelSize;
highp vec2 st = fract(uv);
vec2 uw = vec2(3.0 - 2.0 * st.x, 1.0 + 2.0 * st.x);
vec2 vw = vec2(3.0 - 2.0 * st.y, 1.0 + 2.0 * st.y);
highp vec2 u = vec2((2.0 - st.x) / uw.x - 1.0, st.x / uw.y + 1.0);
highp vec2 v = vec2((2.0 - st.y) / vw.x - 1.0, st.y / vw.y + 1.0);
u *= texelSize.x;
v *= texelSize.y;
float w0 = uw.x * vw.x;
float w1 = uw.y * vw.x;
float w2 = uw.x * vw.y;
float w3 = uw.y * vw.y;
highp vec2 uv0 = base + vec2(u.x, v.x);
highp vec2 uv1 = base + vec2(u.y, v.x);
highp vec2 uv2 = base + vec2(u.x, v.y);
highp vec2 uv3 = base + vec2(u.y, v.y);
uv0 = clamp(uv0, scissorNormalized.xy, scissorNormalized.zw);
uv1 = clamp(uv1, scissorNormalized.xy, scissorNormalized.zw);
uv2 = clamp(uv2, scissorNormalized.xy, scissorNormalized.zw);
uv3 = clamp(uv3, scissorNormalized.xy, scissorNormalized.zw);
float sum = 0.0;
sum += w0 * texture(map, vec4(uv0, layer, position.z));
sum += w1 * texture(map, vec4(uv1, layer, position.z));
sum += w2 * texture(map, vec4(uv2, layer, position.z));
sum += w3 * texture(map, vec4(uv3, layer, position.z));
return sum * 0.0625;
}
float ShadowSample_PCF(const mediump sampler2DArray map,
const highp vec4 scissorNormalized,
const uint layer, const highp vec4 shadowPosition) {
highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w);
position.xy = clamp(position.xy, scissorNormalized.xy, scissorNormalized.zw);
position.z = saturate(position.z);
highp float depth = textureLod(map, vec3(position.xy, layer), 0.0).r;
return step(0.0, position.z - depth);
}
mediump vec2 poissonDisk[64] = vec2[](
vec2(0.511749, 0.547686), vec2(0.58929, 0.257224), vec2(0.165018, 0.57663), vec2(0.407692, 0.742285),
vec2(0.707012, 0.646523), vec2(0.31463, 0.466825), vec2(0.801257, 0.485186), vec2(0.418136, 0.146517),
vec2(0.579889, 0.0368284), vec2(0.79801, 0.140114), vec2(-0.0413185, 0.371455), vec2(-0.0529108, 0.627352),
vec2(0.0821375, 0.882071), vec2(0.17308, 0.301207), vec2(-0.120452, 0.867216), vec2(0.371096, 0.916454),
vec2(-0.178381, 0.146101), vec2(-0.276489, 0.550525), vec2(0.12542, 0.126643), vec2(-0.296654, 0.286879),
vec2(0.261744, -0.00604975), vec2(-0.213417, 0.715776), vec2(0.425684, -0.153211), vec2(-0.480054, 0.321357),
vec2(-0.0717878, -0.0250567), vec2(-0.328775, -0.169666), vec2(-0.394923, 0.130802), vec2(-0.553681, -0.176777),
vec2(-0.722615, 0.120616), vec2(-0.693065, 0.309017), vec2(0.603193, 0.791471), vec2(-0.0754941, -0.297988),
vec2(0.109303, -0.156472), vec2(0.260605, -0.280111), vec2(0.129731, -0.487954), vec2(-0.537315, 0.520494),
vec2(-0.42758, 0.800607), vec2(0.77309, -0.0728102), vec2(0.908777, 0.328356), vec2(0.985341, 0.0759158),
vec2(0.947536, -0.11837), vec2(-0.103315, -0.610747), vec2(0.337171, -0.584), vec2(0.210919, -0.720055),
vec2(0.41894, -0.36769), vec2(-0.254228, -0.49368), vec2(-0.428562, -0.404037), vec2(-0.831732, -0.189615),
vec2(-0.922642, 0.0888026), vec2(-0.865914, 0.427795), vec2(0.706117, -0.311662), vec2(0.545465, -0.520942),
vec2(-0.695738, 0.664492), vec2(0.389421, -0.899007), vec2(0.48842, -0.708054), vec2(0.760298, -0.62735),
vec2(-0.390788, -0.707388), vec2(-0.591046, -0.686721), vec2(-0.769903, -0.413775), vec2(-0.604457, -0.502571),
vec2(-0.557234, 0.00451362), vec2(0.147572, -0.924353), vec2(-0.0662488, -0.892081), vec2(0.863832, -0.407206)
);
const uint DPCF_SHADOW_TAP_COUNT = 12u;
const uint PCSS_SHADOW_BLOCKER_SEARCH_TAP_COUNT = 16u;
const uint PCSS_SHADOW_FILTER_TAP_COUNT = 16u;
float hardenedKernel(float x) {
x = 2.0 * x - 1.0;
float s = sign(x);
x = 1.0 - s * x;
x = x * x * x;
x = s - x * s;
return 0.5 * x + 0.5;
}
highp vec2 computeReceiverPlaneDepthBias(const highp vec3 position) {
highp vec3 duvz_dx = dFdx(position);
highp vec3 duvz_dy = dFdy(position);
highp vec2 dz_duv = inverse(transpose(mat2(duvz_dx.xy, duvz_dy.xy))) * vec2(duvz_dx.z, duvz_dy.z);
return dz_duv;
}
mat2 getRandomRotationMatrix(highp vec2 fragCoord) {
fragCoord += vec2(frameUniforms.temporalNoise);
float randomAngle = interleavedGradientNoise(fragCoord) * (2.0 * PI);
vec2 randomBase = vec2(cos(randomAngle), sin(randomAngle));
mat2 R = mat2(randomBase.x, randomBase.y, -randomBase.y, randomBase.x);
return R;
}
float getPenumbraLs(const bool DIRECTIONAL, const int index, const highp float zLight) {
float penumbra;
if (DIRECTIONAL) {
penumbra = shadowUniforms.shadows[index].bulbRadiusLs;
} else {
penumbra = shadowUniforms.shadows[index].bulbRadiusLs / zLight;
}
return penumbra;
}
float getPenumbraRatio(const bool DIRECTIONAL, const int index,
float z_receiver, float z_blocker) {
float penumbraRatio;
if (DIRECTIONAL) {
penumbraRatio = (z_blocker - z_receiver) / (1.0 - z_blocker);
} else {
float nearOverFarMinusNear = shadowUniforms.shadows[index].nearOverFarMinusNear;
penumbraRatio = (nearOverFarMinusNear + z_blocker) / (nearOverFarMinusNear + z_receiver) - 1.0;
}
return penumbraRatio * frameUniforms.shadowPenumbraRatioScale;
}
void blockerSearchAndFilter(out float occludedCount, out float z_occSum,
const mediump sampler2DArray map, const highp vec4 scissorNormalized, const highp vec2 uv,
const float z_rec, const uint layer,
const highp vec2 filterRadii, const mat2 R, const highp vec2 dz_duv,
const uint tapCount) {
occludedCount = 0.0;
z_occSum = 0.0;
for (uint i = 0u; i < tapCount; i++) {
highp vec2 duv = R * (poissonDisk[i] * filterRadii);
highp vec2 tc = clamp(uv + duv, scissorNormalized.xy, scissorNormalized.zw);
float z_occ = textureLod(map, vec3(tc, layer), 0.0).r;
float z_bias = dot(dz_duv, duv);
float dz = z_occ - z_rec;
float occluded = step(z_bias, dz);
occludedCount += occluded;
z_occSum += z_occ * occluded;
}
}
float filterPCSS(const mediump sampler2DArray map,
const highp vec4 scissorNormalized,
const highp vec2 size,
const highp vec2 uv, const float z_rec, const uint layer,
const highp vec2 filterRadii, const mat2 R, const highp vec2 dz_duv,
const uint tapCount) {
float occludedCount = 0.0;
for (uint i = 0u; i < tapCount; i++) {
highp vec2 duv = R * (poissonDisk[i] * filterRadii);
vec4 d;
highp vec2 tc = clamp(uv + duv, scissorNormalized.xy, scissorNormalized.zw);
highp vec2 st = tc.xy * size - 0.5;
highp vec2 grad = fract(st);
#if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER)
d = textureGather(map, vec3(tc, layer), 0);
#else
d[0] = texelFetchOffset(map, ivec3(st, layer), 0, ivec2(0, 1)).r;
d[1] = texelFetchOffset(map, ivec3(st, layer), 0, ivec2(1, 1)).r;
d[2] = texelFetchOffset(map, ivec3(st, layer), 0, ivec2(1, 0)).r;
d[3] = texelFetchOffset(map, ivec3(st, layer), 0, ivec2(0, 0)).r;
#endif
float z_bias = dot(dz_duv, duv);
vec4 dz = d - vec4(z_rec);
vec4 pcf = step(z_bias, dz);
occludedCount += mix(mix(pcf.w, pcf.z, grad.x), mix(pcf.x, pcf.y, grad.x), grad.y);
}
return occludedCount * (1.0 / float(tapCount));
}
float ShadowSample_DPCF(const bool DIRECTIONAL,
const mediump sampler2DArray map,
const highp vec4 scissorNormalized,
const uint layer, const int index,
const highp vec4 shadowPosition, const highp float zLight) {
highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w);
highp vec2 texelSize = vec2(1.0) / vec2(textureSize(map, 0));
highp vec2 dz_duv = computeReceiverPlaneDepthBias(position);
float penumbra = getPenumbraLs(DIRECTIONAL, index, zLight);
mat2 R = getRandomRotationMatrix(gl_FragCoord.xy);
float occludedCount = 0.0;
float z_occSum = 0.0;
blockerSearchAndFilter(occludedCount, z_occSum,
map, scissorNormalized, position.xy, position.z, layer, texelSize * penumbra, R, dz_duv,
DPCF_SHADOW_TAP_COUNT);
if (z_occSum == 0.0) {
return 1.0;
}
float penumbraRatio = getPenumbraRatio(DIRECTIONAL, index, position.z, z_occSum / occludedCount);
penumbraRatio = saturate(penumbraRatio);
float percentageOccluded = occludedCount * (1.0 / float(DPCF_SHADOW_TAP_COUNT));
percentageOccluded = mix(hardenedKernel(percentageOccluded), percentageOccluded, penumbraRatio);
return 1.0 - percentageOccluded;
}
float ShadowSample_PCSS(const bool DIRECTIONAL,
const mediump sampler2DArray map,
const highp vec4 scissorNormalized,
const uint layer, const int index,
const highp vec4 shadowPosition, const highp float zLight) {
highp vec2 size = vec2(textureSize(map, 0));
highp vec2 texelSize = vec2(1.0) / size;
highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w);
highp vec2 dz_duv = computeReceiverPlaneDepthBias(position);
float penumbra = getPenumbraLs(DIRECTIONAL, index, zLight);
mat2 R = getRandomRotationMatrix(gl_FragCoord.xy);
float occludedCount = 0.0;
float z_occSum = 0.0;
blockerSearchAndFilter(occludedCount, z_occSum,
map, scissorNormalized, position.xy, position.z, layer, texelSize * penumbra, R, dz_duv,
PCSS_SHADOW_BLOCKER_SEARCH_TAP_COUNT);
if (z_occSum == 0.0) {
return 1.0;
}
float penumbraRatio = getPenumbraRatio(DIRECTIONAL, index, position.z, z_occSum / occludedCount);
float percentageOccluded = filterPCSS(map, scissorNormalized, size,
position.xy, position.z, layer,
texelSize * (penumbra * penumbraRatio),
R, dz_duv, PCSS_SHADOW_FILTER_TAP_COUNT);
return 1.0 - percentageOccluded;
}
float chebyshevUpperBound(const highp vec2 moments, const highp float depth,
const highp float minVariance, const highp float lbrAmount) {
if (depth <= moments.x) {
return 1.0;
}
highp float variance = max(moments.y - (moments.x * moments.x), minVariance);
highp float d = depth - moments.x;
highp float p_max = variance / (variance + d * d);
return saturate((p_max - lbrAmount) / (1.0 - lbrAmount));
}
float evaluateEVSM(const bool ELVSM, float c,
const highp vec4 moments, const highp float zReceiver,
const highp vec2 dzduv, const highp vec2 texelSize) {
const highp float EPSILON_MULTIPLIER = 0.002;
float lbrAmount = frameUniforms.vsmLightBleedReduction;
highp vec2 texel_dzduv = dzduv * texelSize;
highp float dz2 = dot(texel_dzduv, texel_dzduv);
highp float depth = zReceiver * 2.0 - 1.0;
highp float pw = exp(c * depth);
highp float epsilon = EPSILON_MULTIPLIER * (pw * pw);
highp float dpwdz = 2.0 * c * pw;
highp float pMinVariance = epsilon + 0.25 * (dpwdz * dpwdz) * dz2;
float p = chebyshevUpperBound(moments.xy, pw, pMinVariance, lbrAmount);
if (ELVSM) {
highp float nw = -1.0 / pw;
highp float epsilon = EPSILON_MULTIPLIER * (nw * nw);
highp float dnwdz = 2.0 * c * nw;
highp float nMinVariance = epsilon + 0.25 * (dnwdz * dnwdz) * dz2;
float n = chebyshevUpperBound(moments.zw, nw, nMinVariance, lbrAmount);
p = min(p, n);
}
return p;
}
float ShadowSample_VSM(const bool DIRECTIONAL, const highp sampler2DArray shadowMap,
const highp vec4 scissorNormalized,
const uint layer, const int index,
const highp vec4 shadowPosition, const highp float zLight) {
bool ELVSM = shadowUniforms.shadows[index].elvsm;
float c = shadowUniforms.shadows[index].vsmExponent;
highp vec2 texelSize = vec2(1.0) / vec2(textureSize(shadowMap, 0));
highp vec3 position = vec3(shadowPosition.xy * (1.0 / shadowPosition.w), shadowPosition.z);
highp vec2 dzduv = computeReceiverPlaneDepthBias(position);
position.xy = clamp(position.xy, scissorNormalized.xy, scissorNormalized.zw);
highp vec4 moments = texture(shadowMap, vec3(position.xy, layer));
return evaluateEVSM(ELVSM, c, moments, position.z, dzduv, texelSize);
}
struct ScreenSpaceRay {
highp vec3 ssRayStart;
highp vec3 ssRayEnd;
highp vec3 ssViewRayEnd;
highp vec3 uvRayStart;
highp vec3 uvRay;
};
void initScreenSpaceRay(out ScreenSpaceRay ray, highp vec3 wsRayStart, vec3 wsRayDirection, float wsRayLength) {
highp mat4 worldToClip = getClipFromWorldMatrix();
highp mat4 viewToClip = getClipFromViewMatrix();
highp vec3 wsRayEnd = wsRayStart + wsRayDirection * wsRayLength;
highp vec4 csRayStart = worldToClip * vec4(wsRayStart, 1.0);
highp vec4 csRayEnd = worldToClip * vec4(wsRayEnd, 1.0);
highp vec4 csViewRayEnd = csRayStart + viewToClip * vec4(0.0, 0.0, wsRayLength, 0.0);
ray.ssRayStart = csRayStart.xyz * (1.0 / csRayStart.w);
ray.ssRayEnd = csRayEnd.xyz * (1.0 / csRayEnd.w);
ray.ssViewRayEnd = csViewRayEnd.xyz * (1.0 / csViewRayEnd.w);
highp vec3 uvRayEnd = vec3(ray.ssRayEnd.xy * 0.5 + 0.5, ray.ssRayEnd.z);
ray.uvRayStart = vec3(ray.ssRayStart.xy * 0.5 + 0.5, ray.ssRayStart.z);
ray.uvRay = uvRayEnd - ray.uvRayStart;
}
float screenSpaceContactShadow(vec3 lightDirection) {
float occlusion = 0.0;
int kStepCount = (frameUniforms.directionalShadows >> 8) & 0xFF;
float kDistanceMax = frameUniforms.ssContactShadowDistance;
ScreenSpaceRay rayData;
initScreenSpaceRay(rayData, shading_position, lightDirection, kDistanceMax);
highp float dt = 1.0 / float(kStepCount);
highp float tolerance = abs(rayData.ssViewRayEnd.z - rayData.ssRayStart.z) * dt;
float dither = interleavedGradientNoise(gl_FragCoord.xy) - 0.5;
highp float t = dt * dither + dt;
highp vec3 ray;
for (int i = 0 ; i < kStepCount ; i++, t += dt) {
ray = rayData.uvRayStart + rayData.uvRay * t;
highp float z = textureLod(sampler0_structure, uvToRenderTargetUV(ray.xy), 0.0).r;
highp float dz = z - ray.z;
if (abs(tolerance - dz) < tolerance) {
occlusion = 1.0;
break;
}
}
vec2 fade = max(12.0 * abs(ray.xy - 0.5) - 5.0, 0.0);
occlusion *= saturate(1.0 - dot(fade, fade));
return occlusion;
}
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
highp vec4 getShadowPosition(const int cascade) {
return getCascadeLightSpacePosition(cascade);
}
#endif
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
highp vec4 getShadowPosition(const int index, const highp vec3 dir, const highp float zLight) {
return getSpotLightSpacePosition(index, dir, zLight);
}
#endif
int getPointLightFace(const highp vec3 r) {
highp vec4 tc;
highp float rx = abs(r.x);
highp float ry = abs(r.y);
highp float rz = abs(r.z);
highp float d = max(rx, max(ry, rz));
if (d == rx) {
return (r.x >= 0.0 ? 0 : 1);
} else if (d == ry) {
return (r.y >= 0.0 ? 2 : 3);
} else {
return (r.z >= 0.0 ? 4 : 5);
}
}
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
void applyShadowStrength(inout float visibility, float strength) {
visibility = 1.0 - (1.0 - visibility) * strength;
}
#endif
float shadow(const bool DIRECTIONAL,
const mediump sampler2DArrayShadow shadowMap,
const int index, highp vec4 shadowPosition, highp float zLight) {
highp vec4 scissorNormalized = shadowUniforms.shadows[index].scissorNormalized;
uint layer = shadowUniforms.shadows[index].layer;
if (CONFIG_SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD) {
return ShadowSample_PCF_Hard(shadowMap, scissorNormalized, layer, shadowPosition);
} else if (CONFIG_SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW) {
return ShadowSample_PCF_Low(shadowMap, scissorNormalized, layer, shadowPosition);
}
return 0.0;
}
float shadow(const bool DIRECTIONAL,
const highp sampler2DArray shadowMap,
const int index, highp vec4 shadowPosition, highp float zLight) {
highp vec4 scissorNormalized = shadowUniforms.shadows[index].scissorNormalized;
uint layer = shadowUniforms.shadows[index].layer;
if (frameUniforms.shadowSamplingType == SHADOW_SAMPLING_RUNTIME_EVSM) {
return ShadowSample_VSM(DIRECTIONAL, shadowMap, scissorNormalized, layer, index,
shadowPosition, zLight);
}
if (frameUniforms.shadowSamplingType == SHADOW_SAMPLING_RUNTIME_DPCF) {
return ShadowSample_DPCF(DIRECTIONAL, shadowMap, scissorNormalized, layer, index,
shadowPosition, zLight);
}
if (frameUniforms.shadowSamplingType == SHADOW_SAMPLING_RUNTIME_PCSS) {
return ShadowSample_PCSS(DIRECTIONAL, shadowMap, scissorNormalized, layer, index,
shadowPosition, zLight);
}
if (frameUniforms.shadowSamplingType == SHADOW_SAMPLING_RUNTIME_PCF) {
return ShadowSample_PCF(shadowMap, scissorNormalized, layer,
shadowPosition);
}
return 0.0;
}
#if defined(HAS_ATTRIBUTE_COLOR)
LAYOUT_LOCATION(4) VARYING mediump vec4 vertex_color;
#endif
#if defined(HAS_ATTRIBUTE_UV0) && !defined(HAS_ATTRIBUTE_UV1)
LAYOUT_LOCATION(5) VARYING highp vec2 vertex_uv01;
#elif defined(HAS_ATTRIBUTE_UV1)
LAYOUT_LOCATION(5) VARYING highp vec4 vertex_uv01;
#endif
LAYOUT_LOCATION(6) VARYING highp vec4 vertex_worldPosition;
#if defined(HAS_ATTRIBUTE_TANGENTS)
LAYOUT_LOCATION(7) SHADING_INTERPOLATION VARYING mediump vec3 vertex_worldNormal;
#if defined(MATERIAL_NEEDS_TBN)
LAYOUT_LOCATION(8) SHADING_INTERPOLATION VARYING mediump vec4 vertex_worldTangent;
#endif
#endif
LAYOUT_LOCATION(9) VARYING highp vec4 vertex_position;
#if defined(FILAMENT_HAS_FEATURE_INSTANCING)
LAYOUT_LOCATION(10) flat VARYING highp int instance_index;
highp int logical_instance_index;
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
LAYOUT_LOCATION(11) VARYING highp vec4 vertex_lightSpacePosition;
#endif
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
#if defined(GL_ES) && defined(FILAMENT_GLSLANG)
LAYOUT_LOCATION(100) out float filament_gl_ClipDistance[2];
#define FILAMENT_CLIPDISTANCE filament_gl_ClipDistance
#else
#define FILAMENT_CLIPDISTANCE gl_ClipDistance
#endif
#endif
vec3 vignette(const vec3 color, const highp vec2 uv, const vec4 vignette, const vec4 vignetteColor) {
float midPoint = vignette.x;
float radius = vignette.y;
float aspect = vignette.z;
float feather = vignette.w;
vec2 distance = abs(uv - 0.5) * midPoint;
distance.x *= aspect;
distance = pow(saturate(distance), vec2(radius));
float amount = pow(saturate(1.0 - dot(distance, distance)), feather * 5.0);
return color * mix(vignetteColor.rgb, vec3(1.0), amount);
}