4 // https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/glTF-WebGL-PBR/shaders/pbr-frag.glsl
5 // Commit dc84b5e374fb3d23153d2248a338ef88173f9eb6
7 // This fragment shader defines a reference implementation for Physically Based Shading of
8 // a microfacet surface material defined by a glTF model.For the DamagedHelmet.gltf and its Assets
11 // [1] Real Shading in Unreal Engine 4
12 // http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
13 // [2] Physically Based Shading at Disney
14 // http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
15 // [3] README.md - Environment Maps
16 // https://github.com/KhronosGroup/glTF-Sample-Viewer/#environment-maps
17 // [4] \"An Inexpensive BRDF Model for Physically based Rendering\" by Christophe Schlick
18 // https://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf
21 precision highp float;
23 precision mediump float;
32 #endif //GLTF_CHANNELS
34 uniform lowp vec4 uColor; // Color from SceneGraph
35 uniform lowp vec4 uColorFactor; // Color from material
36 uniform lowp float uMetallicFactor;
37 uniform lowp float uRoughnessFactor;
38 uniform lowp float uDielectricSpecular;
42 uniform sampler2D sAlbedoAlpha;
43 #endif // BASECOLOR_TEX
44 #ifdef METALLIC_ROUGHNESS_TEX
45 uniform sampler2D sMetalRoughness;
46 #endif // METALLIC_ROUGHNESS_TEX
48 uniform sampler2D sNormal;
49 uniform float uNormalScale;
52 uniform sampler2D sAlbedoMetal;
53 uniform sampler2D sNormalRoughness;
57 uniform sampler2D sOcclusion;
58 uniform float uOcclusionStrength;
61 #ifdef EMISSIVE_TEXTURE
62 uniform sampler2D sEmissive;
64 uniform vec3 uEmissiveFactor;
66 uniform float uSpecularFactor;
67 uniform vec3 uSpecularColorFactor;
68 #ifdef MATERIAL_SPECULAR_TEXTURE
69 uniform sampler2D sSpecular;
71 #ifdef MATERIAL_SPECULAR_COLOR_TEXTURE
72 uniform sampler2D sSpecularColor;
75 // For Light (Currently Directional Only)
77 uniform mediump int uLightCount;
78 uniform mediump vec3 uLightDirection[MAX_LIGHTS];
79 uniform mediump vec3 uLightColor[MAX_LIGHTS];
82 uniform lowp int uIsShadowEnabled;
83 uniform sampler2D sShadowMap;
84 in highp vec3 positionFromLightView;
87 uniform sampler2D sbrdfLUT;
88 uniform samplerCube sDiffuseEnvSampler;
89 uniform samplerCube sSpecularEnvSampler;
90 uniform float uIblIntensity;
91 uniform mediump vec3 uYDirection;
92 uniform float uMaxLOD;
95 uniform lowp float uOpaque;
96 uniform lowp float uMask;
97 uniform lowp float uAlphaThreshold;
99 // TODO: Multiple texture coordinate will be supported.
103 in highp vec3 vPositionToCamera;
107 const float c_MinRoughness = 0.04;
108 const float M_PI = 3.141592653589793;
110 // These properties can be used for circular sampling for PCF
112 // Percentage Closer Filtering to mitigate the banding artifacts.
113 const int kPcfSampleCount = 9;
115 const float kPi = 3.141592653589f;
116 const float kInvSampleCount = 1.0 / float(kPcfSampleCount);
117 const float kPcfTheta = 2.f * kPi * kInvSampleCount;
118 const float kSinPcfTheta = sin(kPcfTheta);
119 const float kCosPcfTheta = cos(kPcfTheta);
121 uniform lowp int uEnableShadowSoftFiltering;
122 uniform mediump float uShadowIntensity;
123 uniform mediump float uShadowBias;
125 vec3 linear(vec3 color)
127 return pow(color, vec3(2.2));
132 // Metallic and Roughness material properties are packed together
133 // In glTF, these factors can be specified by fixed scalar values
134 // or from a metallic-roughness map
135 // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
136 // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
137 lowp float metallic = uMetallicFactor;
138 lowp float perceptualRoughness = uRoughnessFactor;
139 // If there isn't normal texture, use surface normal
140 highp vec3 n = normalize(vTBN[2].xyz);
143 // The albedo may be defined from a base texture or a flat color
145 lowp vec4 baseColor = texture(sAlbedoAlpha, vUV);
146 baseColor = vColor * vec4(linear(baseColor.rgb), baseColor.w) * uColorFactor;
147 #else // BASECOLOR_TEX
148 lowp vec4 baseColor = vColor * uColorFactor;
149 #endif // BASECOLOR_TEX
151 #ifdef METALLIC_ROUGHNESS_TEX
152 lowp vec4 metrou = texture(sMetalRoughness, vUV);
153 metallic = metrou.METALLIC * metallic;
154 perceptualRoughness = metrou.ROUGHNESS * perceptualRoughness;
155 #endif // METALLIC_ROUGHNESS_TEX
158 n = texture(sNormal, vUV).rgb;
159 n = normalize(vTBN * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
162 vec4 albedoMetal = texture(sAlbedoMetal, vUV);
163 lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * vColor * uColorFactor;
165 metallic = albedoMetal.METALLIC * metallic;
167 vec4 normalRoughness = texture(sNormalRoughness, vUV);
168 perceptualRoughness = normalRoughness.ROUGHNESS * perceptualRoughness;
170 n = normalRoughness.rgb;
171 n = normalize(vTBN * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
174 // The value of uOpaque and uMask can be 0.0 or 1.0.
175 // If uMask is 1.0, a Pixel that has bigger alpha than uAlphaThreashold becomes fully opaque,
176 // and, a pixel that has smaller alpha than uAlphaThreashold becomes fully transparent.
177 // If uOpaque is 1.0, alpha value of final color is 1.0;
178 // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_material_alphamode
179 if(uMask > 0.5 && baseColor.a < uAlphaThreshold)
183 baseColor.a = mix(baseColor.a, 1.0, uOpaque);
185 metallic = clamp(metallic, 0.0, 1.0);
186 // Roughness is authored as perceptual roughness; as is convention,
187 // convert to material roughness by squaring the perceptual roughness [2].
188 perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
191 lowp vec3 f0 = vec3(uDielectricSpecular);
194 float specularWeight = 1.0;
195 vec4 materialSpecularTexture = vec4(1.0);
196 #ifdef MATERIAL_SPECULAR_TEXTURE
197 materialSpecularTexture.a = texture(sSpecular, vUV).a;
199 #ifdef MATERIAL_SPECULAR_COLOR_TEXTURE
200 materialSpecularTexture.rgb = texture(sSpecularColor, vUV).rgb;
202 specularWeight = uSpecularFactor * materialSpecularTexture.a;
203 f0 = min(f0 * uSpecularColorFactor * materialSpecularTexture.rgb, vec3(1.0));
204 f0 = mix(f0, baseColor.rgb, metallic);
206 mediump vec3 v = normalize(vPositionToCamera); // Vector from surface point to camera
207 mediump float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
208 mediump vec3 reflection = -normalize(reflect(v, n));
209 lowp vec3 brdf = texture(sbrdfLUT, vec2(NdotV, 1.0 - perceptualRoughness)).rgb;
210 vec3 Fr = max(vec3(1.0 - perceptualRoughness), f0) - f0;
211 vec3 k_S = f0 + Fr * pow(1.0 - NdotV, 5.0);
212 vec3 FssEss = specularWeight * (k_S * brdf.x + brdf.y);
215 // uMaxLOD that means mipmap level of specular texture is used for bluring of reflection of specular following roughness.
216 float lod = perceptualRoughness * (uMaxLOD - 1.0);
217 lowp vec3 specularLight = linear(textureLod(sSpecularEnvSampler, reflection * uYDirection, lod).rgb);
218 lowp vec3 specular = specularLight * FssEss;
221 lowp vec3 diffuseColor = mix(baseColor.rgb, vec3(0), metallic);
222 lowp vec3 irradiance = linear(texture(sDiffuseEnvSampler, n * uYDirection).rgb);
223 float Ems = (1.0 - (brdf.x + brdf.y));
224 vec3 F_avg = specularWeight * (f0 + (1.0 - f0) / 21.0);
225 vec3 FmsEms = Ems * FssEss * F_avg / (1.0 - F_avg * Ems);
226 vec3 k_D = diffuseColor * (1.0 - FssEss + FmsEms);
227 lowp vec3 diffuse = (FmsEms + k_D) * irradiance;
229 lowp vec3 color = (diffuse + specular) * uIblIntensity;
234 // Compute reflectance.
235 lowp float reflectance = max(max(f0.r, f0.g), f0.b);
236 lowp float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
237 lowp float r = perceptualRoughness * perceptualRoughness;
238 lowp float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
239 mediump float roughnessSq = r * r;
240 lowp vec3 diffuseColorPunctual = baseColor.rgb * (vec3(1.0) - f0);
241 diffuseColorPunctual *= ( 1.0 - metallic );
243 for(int i = 0; i < uLightCount; ++i)
245 highp vec3 l = normalize(-uLightDirection[i]); // Vector from surface point to light
246 mediump vec3 h = normalize(l+v); // Half vector between both l and v
247 mediump float VdotH = dot(v, h);
248 lowp vec3 specularReflection = f0 + (reflectance90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
250 mediump float NdotL = clamp(dot(n, l), 0.001, 1.0);
251 lowp float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
252 lowp float geometricOcclusion = attenuationL * attenuationV;
254 highp float NdotH = dot(n, h);
255 highp float f = (NdotH * roughnessSq - NdotH) * NdotH + 1.0;
256 lowp float microfacetDistribution = roughnessSq / (M_PI * f * f);;
258 // Calculation of analytical lighting contribution
259 lowp vec3 diffuseContrib = ( 1.0 - specularReflection ) * ( diffuseColorPunctual / M_PI );
260 lowp vec3 specContrib = specularReflection * geometricOcclusion * microfacetDistribution / ( 4.0 * NdotL * NdotV );
262 // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
263 color += NdotL * uLightColor[i] * (diffuseContrib + specContrib);
267 if(float(uIsShadowEnabled) * uShadowIntensity > 0.0)
269 mediump float exposureFactor = 0.0;
270 if(uEnableShadowSoftFiltering > 0)
272 ivec2 texSize = textureSize(sShadowMap, 0);
273 mediump vec2 texelSize = vec2(1.0) / vec2(texSize.x, texSize.y);
274 mediump vec2 pcfSample = vec2(1.f, 0.f);
275 for (int i = 0; i < kPcfSampleCount; ++i)
277 pcfSample = vec2(kCosPcfTheta * pcfSample.x - kSinPcfTheta * pcfSample.y,
278 kSinPcfTheta * pcfSample.x + kCosPcfTheta * pcfSample.y);
279 lowp float depthValue = texture(sShadowMap, positionFromLightView.xy + pcfSample * texelSize).r;
280 exposureFactor += (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
282 exposureFactor *= kInvSampleCount;
286 mediump float depthValue = texture(sShadowMap, positionFromLightView.xy).r;
287 exposureFactor = (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
289 color *= (1.0 - (1.0 - exposureFactor) * uShadowIntensity);
293 lowp float ao = texture(sOcclusion, vUV).r;
294 color = mix(color, color * ao, uOcclusionStrength);
297 #ifdef EMISSIVE_TEXTURE
298 lowp vec3 emissive = linear(texture(sEmissive, vUV).rgb) * uEmissiveFactor;
300 lowp vec3 emissive = uEmissiveFactor;
301 #endif // EMISSIVE_TEXTURE
304 FragColor = vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a) * uColor;