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;
33 #endif //GLTF_CHANNELS
36 uniform lowp vec4 uColor; // Color from SceneGraph
37 uniform lowp vec4 uColorFactor; // Color from material
38 uniform lowp float uMetallicFactor;
39 uniform lowp float uRoughnessFactor;
43 uniform sampler2D sAlbedoAlpha;
44 #endif // BASECOLOR_TEX
45 #ifdef METALLIC_ROUGHNESS_TEX
46 uniform sampler2D sMetalRoughness;
47 #endif // METALLIC_ROUGHNESS_TEX
49 uniform sampler2D sNormal;
50 uniform float uNormalScale;
53 uniform sampler2D sAlbedoMetal;
54 uniform sampler2D sNormalRoughness;
58 uniform sampler2D sOcclusion;
59 uniform float uOcclusionStrength;
63 uniform sampler2D sEmissive;
64 uniform vec3 uEmissiveFactor;
68 uniform sampler2D sbrdfLUT;
69 uniform samplerCube sDiffuseEnvSampler;
70 uniform samplerCube sSpecularEnvSampler;
71 uniform float uIblIntensity;
72 uniform vec3 uYDirection;
75 uniform lowp float uOpaque;
76 uniform lowp float uMask;
77 uniform lowp float uAlphaThreshold;
79 // TODO: Multiple texture coordinate will be supported.
83 in highp vec3 vPositionToCamera;
89 mediump float NdotL; // cos angle between normal and light direction
90 mediump float NdotV; // cos angle between normal and view direction
91 mediump float NdotH; // cos angle between normal and half vector
92 mediump float VdotH; // cos angle between view direction and half vector
93 mediump vec3 reflectance0; // full reflectance color (normal incidence angle)
94 mediump vec3 reflectance90; // reflectance color at grazing angle
95 lowp float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2])
98 const float M_PI = 3.141592653589793;
99 const float c_MinRoughness = 0.04;
101 vec3 specularReflection(PBRInfo pbrInputs)
103 return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
106 float geometricOcclusion(PBRInfo pbrInputs)
108 mediump float NdotL = pbrInputs.NdotL;
109 mediump float NdotV = pbrInputs.NdotV;
110 lowp float r = pbrInputs.alphaRoughness;
112 lowp float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
113 lowp float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
114 return attenuationL * attenuationV;
117 float microfacetDistribution(PBRInfo pbrInputs)
119 mediump float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
120 lowp float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
121 return roughnessSq / (M_PI * f * f);
124 vec3 linear(vec3 color)
126 return pow(color, vec3(2.2));
131 // Metallic and Roughness material properties are packed together
132 // In glTF, these factors can be specified by fixed scalar values
133 // or from a metallic-roughness map
134 // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
135 // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
136 lowp float metallic = uMetallicFactor;
137 lowp float perceptualRoughness = uRoughnessFactor;
138 // If there isn't normal texture, use surface normal
139 mediump vec3 n = normalize(vTBN[2].xyz);
142 // The albedo may be defined from a base texture or a flat color
144 lowp vec4 baseColor = texture(sAlbedoAlpha, vUV);
145 baseColor = vec4(linear(baseColor.rgb), baseColor.w) * uColorFactor;
146 #else // BASECOLOR_TEX
147 lowp vec4 baseColor = vColor * uColorFactor;
148 #endif // BASECOLOR_TEX
150 #ifdef METALLIC_ROUGHNESS_TEX
151 lowp vec4 metrou = texture(sMetalRoughness, vUV);
152 metallic = metrou.METALLIC * metallic;
153 perceptualRoughness = metrou.ROUGHNESS * perceptualRoughness;
154 #endif // METALLIC_ROUGHNESS_TEX
157 n = texture(sNormal, vUV).rgb;
158 n = normalize(vTBN * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
161 vec4 albedoMetal = texture(sAlbedoMetal, vUV);
162 lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * vColor * uColorFactor;
164 metallic = albedoMetal.METALLIC * metallic;
166 vec4 normalRoughness = texture(sNormalRoughness, vUV);
167 perceptualRoughness = normalRoughness.ROUGHNESS * perceptualRoughness;
169 n = normalRoughness.rgb;
170 n = normalize(vTBN * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
173 // The value of uOpaque and uMask can be 0.0 or 1.0.
174 // If uOpaque is 1.0, alpha value of final color is 1.0;
175 // If uOpaque is 0.0 and uMask is 1.0, alpha value of final color is 0.0 when input alpha is lower than uAlphaThreshold or
176 // 1.0 when input alpha is larger than uAlphaThreshold.
177 // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_material_alphamode
178 baseColor.a = mix(baseColor.a, 1.0, uOpaque);
179 baseColor.a = min(mix(baseColor.a, floor(baseColor.a - uAlphaThreshold + 1.0), uMask), 1.0);
181 metallic = clamp(metallic, 0.0, 1.0);
182 // Roughness is authored as perceptual roughness; as is convention,
183 // convert to material roughness by squaring the perceptual roughness [2].
184 perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
185 lowp float alphaRoughness = perceptualRoughness * perceptualRoughness;
187 lowp vec3 f0 = vec3(0.04);
188 lowp vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
189 diffuseColor *= (1.0 - metallic);
190 lowp vec3 specularColor = mix(f0, baseColor.rgb, metallic);
192 // Compute reflectance.
193 lowp float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
195 // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
196 // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
197 lowp float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
198 lowp vec3 specularEnvironmentR0 = specularColor.rgb;
199 lowp vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
201 mediump vec3 v = normalize(vPositionToCamera); // Vector from surface point to camera
202 mediump float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
203 mediump vec3 reflection = -normalize(reflect(v, n));
205 lowp vec3 color = vec3(0.0);
206 lowp vec3 diffuseLight = linear(texture(sDiffuseEnvSampler, n * uYDirection).rgb);
207 lowp vec3 specularLight = linear(texture(sSpecularEnvSampler, reflection * uYDirection).rgb);
208 // retrieve a scale and bias to F0. See [1], Figure 3
209 lowp vec3 brdf = linear(texture(sbrdfLUT, vec2(NdotV, 1.0 - perceptualRoughness)).rgb);
211 lowp vec3 diffuse = diffuseLight * diffuseColor;
212 lowp vec3 specular = specularLight * (specularColor * brdf.x + brdf.y);
213 color += (diffuse + specular) * uIblIntensity;
216 lowp float ao = texture(sOcclusion, vUV).r;
217 color = mix(color, color * ao, uOcclusionStrength);
221 lowp vec3 emissive = linear(texture(sEmissive, vUV).rgb) * uEmissiveFactor;
225 FragColor = vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a) * uColor;