diff --git a/src/renderer.js b/src/renderer.js index 25b277b3..6e59bcec 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -10,6 +10,7 @@ import metallicRoughnessShader from './shaders/metallic-roughness.frag'; import primitiveShader from './shaders/primitive.vert'; import texturesShader from './shaders/textures.glsl'; import tonemappingShader from'./shaders/tonemapping.glsl'; +import shaderFunctions from './shaders/functions.glsl'; class gltfRenderer { @@ -29,6 +30,7 @@ class gltfRenderer shaderSources.set("metallic-roughness.frag", metallicRoughnessShader); shaderSources.set("tonemapping.glsl", tonemappingShader); shaderSources.set("textures.glsl", texturesShader); + shaderSources.set("functions.glsl", shaderFunctions); this.shaderCache = new ShaderCache(shaderSources); diff --git a/src/shaders/functions.glsl b/src/shaders/functions.glsl new file mode 100644 index 00000000..0fdf9778 --- /dev/null +++ b/src/shaders/functions.glsl @@ -0,0 +1,132 @@ +// textures.glsl needs to be included + +const float M_PI = 3.141592653589793; +const float c_MinReflectance = 0.04; + +varying vec3 v_Position; + +#ifdef HAS_NORMALS +#ifdef HAS_TANGENTS +varying mat3 v_TBN; +#else +varying vec3 v_Normal; +#endif +#endif + +#ifdef HAS_VERTEX_COLOR_VEC3 +varying vec3 v_Color; +#endif +#ifdef HAS_VERTEX_COLOR_VEC4 +varying vec4 v_Color; +#endif + +struct AngularInfo +{ + float NdotL; // cos angle between normal and light direction + float NdotV; // cos angle between normal and view direction + float NdotH; // cos angle between normal and half vector + float LdotH; // cos angle between light direction and half vector + + float VdotH; // cos angle between view direction and half vector + + vec3 padding; +}; + +vec4 getVertexColor() +{ + vec4 color = vec4(1.0, 1.0, 1.0, 1.0); + +#ifdef HAS_VERTEX_COLOR_VEC3 + color.rgb = v_Color; +#endif +#ifdef HAS_VERTEX_COLOR_VEC4 + color = v_Color; +#endif + + return color; +} + +// Find the normal for this fragment, pulling either from a predefined normal map +// or from the interpolated mesh normal and tangent attributes. +vec3 getNormal() +{ + vec2 UV = getNormalUV(); + + // Retrieve the tangent space matrix +#ifndef HAS_TANGENTS + vec3 pos_dx = dFdx(v_Position); + vec3 pos_dy = dFdy(v_Position); + vec3 tex_dx = dFdx(vec3(UV, 0.0)); + vec3 tex_dy = dFdy(vec3(UV, 0.0)); + vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t); + +#ifdef HAS_NORMALS + vec3 ng = normalize(v_Normal); +#else + vec3 ng = cross(pos_dx, pos_dy); +#endif + + t = normalize(t - ng * dot(ng, t)); + vec3 b = normalize(cross(ng, t)); + mat3 tbn = mat3(t, b, ng); +#else // HAS_TANGENTS + mat3 tbn = v_TBN; +#endif + +#ifdef HAS_NORMAL_MAP + vec3 n = texture2D(u_NormalSampler, UV).rgb; + n = normalize(tbn * ((2.0 * n - 1.0) * vec3(u_NormalScale, u_NormalScale, 1.0))); +#else + // The tbn matrix is linearly interpolated, so we need to re-normalize + vec3 n = normalize(tbn[2].xyz); +#endif + + return n; +} + +float getPerceivedBrightness(vec3 vector) +{ + return sqrt(0.299 * vector.r * vector.r + 0.587 * vector.g * vector.g + 0.114 * vector.b * vector.b); +} + +// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows/js/three.pbrUtilities.js#L34 +float solveMetallic(vec3 diffuse, vec3 specular, float oneMinusSpecularStrength) { + float specularBrightness = getPerceivedBrightness(specular); + + if (specularBrightness < c_MinReflectance) { + return 0.0; + } + + float diffuseBrightness = getPerceivedBrightness(diffuse); + + float a = c_MinReflectance; + float b = diffuseBrightness * oneMinusSpecularStrength / (1.0 - c_MinReflectance) + specularBrightness - 2.0 * c_MinReflectance; + float c = c_MinReflectance - specularBrightness; + float D = b * b - 4.0 * a * c; + + return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); +} + +AngularInfo getAngularInfo(vec3 pointToLight, vec3 normal, vec3 view) +{ + // Standard one-letter names + vec3 n = normalize(normal); // Outward direction of surface point + vec3 v = normalize(view); // Direction from surface point to view + vec3 l = normalize(pointToLight); // Direction from surface point to light + vec3 h = normalize(l + v); // Direction of the vector between l and v + + float NdotL = clamp(dot(n, l), 0.0, 1.0); + float NdotV = clamp(dot(n, v), 0.0, 1.0); + float NdotH = clamp(dot(n, h), 0.0, 1.0); + float LdotH = clamp(dot(l, h), 0.0, 1.0); + float VdotH = clamp(dot(v, h), 0.0, 1.0); + + return AngularInfo( + NdotL, + NdotV, + NdotH, + LdotH, + VdotH, + vec3(0, 0, 0) + ); +} diff --git a/src/shaders/metallic-roughness.frag b/src/shaders/metallic-roughness.frag index 2cdd96e4..5e69ac0d 100644 --- a/src/shaders/metallic-roughness.frag +++ b/src/shaders/metallic-roughness.frag @@ -23,6 +23,7 @@ precision highp float; #include #include +#include // KHR_lights_punctual extension. // see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual @@ -73,45 +74,6 @@ uniform vec3 u_Camera; // debugging flags used for shader output of intermediate PBR variables uniform vec4 u_ScaleIBLAmbient; -// -// - -varying vec3 v_Position; - -#ifdef HAS_NORMALS -#ifdef HAS_TANGENTS -varying mat3 v_TBN; -#else -varying vec3 v_Normal; -#endif -#endif - -#ifdef HAS_VERTEX_COLOR_VEC3 -varying vec3 v_Color; -#endif -#ifdef HAS_VERTEX_COLOR_VEC4 -varying vec4 v_Color; -#endif - -// -// - -// Encapsulate the various inputs used by the various functions in the shading equation -// We store values in this struct to simplify the integration of alternative implementations -// of the shading terms, outlined in the Readme.md Appendix. - -struct AngularInfo -{ - float NdotL; // cos angle between normal and light direction - float NdotV; // cos angle between normal and view direction - float NdotH; // cos angle between normal and half vector - float LdotH; // cos angle between light direction and half vector - - float VdotH; // cos angle between view direction and half vector - - vec3 padding; -}; - struct MaterialInfo { float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) @@ -128,61 +90,6 @@ struct MaterialInfo float padding; }; -const float M_PI = 3.141592653589793; -const float c_MinReflectance = 0.04; - -vec4 getVertexColor() -{ - vec4 color = vec4(1.0, 1.0, 1.0, 1.0); - -#ifdef HAS_VERTEX_COLOR_VEC3 - color.rgb = v_Color; -#endif -#ifdef HAS_VERTEX_COLOR_VEC4 - color = v_Color; -#endif - - return color; -} - -// Find the normal for this fragment, pulling either from a predefined normal map -// or from the interpolated mesh normal and tangent attributes. -vec3 getNormal() -{ - vec2 UV = getNormalUV(); - - // Retrieve the tangent space matrix -#ifndef HAS_TANGENTS - vec3 pos_dx = dFdx(v_Position); - vec3 pos_dy = dFdy(v_Position); - vec3 tex_dx = dFdx(vec3(UV, 0.0)); - vec3 tex_dy = dFdy(vec3(UV, 0.0)); - vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t); - -#ifdef HAS_NORMALS - vec3 ng = normalize(v_Normal); -#else - vec3 ng = cross(pos_dx, pos_dy); -#endif - - t = normalize(t - ng * dot(ng, t)); - vec3 b = normalize(cross(ng, t)); - mat3 tbn = mat3(t, b, ng); -#else // HAS_TANGENTS - mat3 tbn = v_TBN; -#endif - -#ifdef HAS_NORMAL_MAP - vec3 n = texture2D(u_NormalSampler, UV).rgb; - n = normalize(tbn * ((2.0 * n - 1.0) * vec3(u_NormalScale, u_NormalScale, 1.0))); -#else - // The tbn matrix is linearly interpolated, so we need to re-normalize - vec3 n = normalize(tbn[2].xyz); -#endif - - return n; -} - // Calculation of the lighting contribution from an optional Image Based Light source. // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1]. // See our README.md on Environment Maps [3] for additional discussion. @@ -269,53 +176,6 @@ float microfacetDistribution(MaterialInfo materialInfo, AngularInfo angularInfo) return roughnessSq / (M_PI * f * f); } -float getPerceivedBrightness(vec3 vector) -{ - return sqrt(0.299 * vector.r * vector.r + 0.587 * vector.g * vector.g + 0.114 * vector.b * vector.b); -} - -// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows/js/three.pbrUtilities.js#L34 -float solveMetallic(vec3 diffuse, vec3 specular, float oneMinusSpecularStrength) { - float specularBrightness = getPerceivedBrightness(specular); - - if (specularBrightness < c_MinReflectance) { - return 0.0; - } - - float diffuseBrightness = getPerceivedBrightness(diffuse); - - float a = c_MinReflectance; - float b = diffuseBrightness * oneMinusSpecularStrength / (1.0 - c_MinReflectance) + specularBrightness - 2.0 * c_MinReflectance; - float c = c_MinReflectance - specularBrightness; - float D = b * b - 4.0 * a * c; - - return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); -} - -AngularInfo getAngularInfo(vec3 pointToLight, vec3 normal, vec3 view) -{ - // Standard one-letter names - vec3 n = normalize(normal); // Outward direction of surface point - vec3 v = normalize(view); // Direction from surface point to view - vec3 l = normalize(pointToLight); // Direction from surface point to light - vec3 h = normalize(l + v); // Direction of the vector between l and v - - float NdotL = clamp(dot(n, l), 0.0, 1.0); - float NdotV = clamp(dot(n, v), 0.0, 1.0); - float NdotH = clamp(dot(n, h), 0.0, 1.0); - float LdotH = clamp(dot(l, h), 0.0, 1.0); - float VdotH = clamp(dot(v, h), 0.0, 1.0); - - return AngularInfo( - NdotL, - NdotV, - NdotH, - LdotH, - VdotH, - vec3(0, 0, 0) - ); -} - vec3 getPointShade(vec3 pointToLight, MaterialInfo materialInfo, vec3 normal, vec3 view) { AngularInfo angularInfo = getAngularInfo(pointToLight, normal, view);