From 259b26167668278b9fbf2efba69b2e16ca7aa6fb Mon Sep 17 00:00:00 2001 From: Scott Nagy Date: Fri, 9 Jun 2017 16:17:17 -0700 Subject: [PATCH] Add attribution for the shader equations, also some misc cleanup Rename the "normal matrix" to better reflect its usage as a transform from model -> world Don't modify the gl context, instead store information about extensions in the state move the camera/view calculations outside of the per-mesh loop Add information about which papers the different lighting information came from to the shader. --- main.js | 7 +++---- scene.js | 40 +++++++++++++++++++-------------------- shaders/pbr-frag.glsl | 44 +++++++++++++++++++++++++------------------ shaders/pbr-vert.glsl | 12 ++++++------ 4 files changed, 55 insertions(+), 48 deletions(-) diff --git a/main.js b/main.js index b8380945..95bdfee5 100644 --- a/main.js +++ b/main.js @@ -136,9 +136,6 @@ function init(vertSource, fragSource) { var ctx2d = canvas2d.getContext("2d"); - // Load extensions - gl.hasLodExt = gl.getExtension('EXT_shader_texture_lod'); - gl.hasDerivativesExt = gl.getExtension('OES_standard_derivatives'); var hasSRGBExt = gl.getExtension('EXT_SRGB'); glState = { @@ -147,6 +144,8 @@ function init(vertSource, fragSource) { vertSource: vertSource, fragSource: fragSource, scene: null, + hasLODExtension:gl.getExtension('EXT_shader_texture_lod'), + hasDerivativesExtension:gl.getExtension('OES_standard_derivatives'), sRGBifAvailable: (hasSRGBExt ? hasSRGBExt.SRGB_EXT : gl.RGBA) }; @@ -170,7 +169,7 @@ function init(vertSource, fragSource) { // Get location of mvp matrix uniform glState.uniforms['u_mvpMatrix'] = { 'funcName': 'uniformMatrix4fv' }; // Get location of normal matrix uniform - glState.uniforms['u_NormalMatrix'] = { 'funcName': 'uniformMatrix4fv' }; + glState.uniforms['u_modelMatrix'] = { 'funcName': 'uniformMatrix4fv' }; // Light glState.uniforms['u_LightDirection'] = { 'funcName': 'uniform3f', 'vals': [0.0, 0.5, 0.5] }; diff --git a/scene.js b/scene.js index c92cd27f..974c61e7 100644 --- a/scene.js +++ b/scene.js @@ -49,7 +49,7 @@ class Mesh { } var imageInfos = this.initTextures(gl, gltf); - this.initProgram(gl); + this.initProgram(gl, globalState); this.accessorsLoading = 0; // Attributes @@ -65,7 +65,7 @@ class Mesh { } - initProgram(gl) { + initProgram(gl, globalState) { var definesToString = function(defines) { var outStr = ''; for (var def in defines) { @@ -75,7 +75,7 @@ class Mesh { }; var shaderDefines = definesToString(this.defines);//"#define USE_SAVED_TANGENTS 1\n#define USE_MATHS 1\n#define USE_IBL 1\n"; - if (gl.hasLodExt) { + if (globalState.hasLODExtension) { shaderDefines += '#define USE_TEX_LOD 1\n'; } @@ -111,28 +111,12 @@ class Mesh { var modelMatrix = mat4.create(); mat4.multiply(modelMatrix, modelMatrix, transform); - // Update view matrix - // roll, pitch and translate are all globals. :) - var xRotation = mat4.create(); - mat4.rotateY(xRotation, xRotation, roll); - var yRotation = mat4.create(); - mat4.rotateX(yRotation, yRotation, pitch); - view = mat4.create(); - mat4.multiply(view, yRotation, xRotation); - view[14] = -translate; - if (this.material.doubleSided) { gl.disable(gl.CULL_FACE); } else { gl.enable(gl.CULL_FACE); } - // set this outside of this function - var cameraPos = [view[14] * Math.sin(roll) * Math.cos(-pitch), - view[14] * Math.sin(-pitch), - -view[14] * Math.cos(roll) * Math.cos(-pitch)]; - globalState.uniforms['u_Camera'].vals = cameraPos; - // Update mvp matrix var mvMatrix = mat4.create(); var mvpMatrix = mat4.create(); @@ -142,7 +126,7 @@ class Mesh { globalState.uniforms['u_mvpMatrix'].vals = [false, mvpMatrix]; // Update normal matrix - globalState.uniforms['u_NormalMatrix'].vals = [false, modelMatrix]; + globalState.uniforms['u_modelMatrix'].vals = [false, modelMatrix]; applyState(gl, this.program, globalState, this.glState); @@ -353,6 +337,22 @@ class Scene { } }; + // set up the camera position and view matrix + var cameraPos = [-translate * Math.sin(roll) * Math.cos(-pitch), + -translate * Math.sin(-pitch), + translate * Math.cos(roll) * Math.cos(-pitch)]; + this.globalState.uniforms['u_Camera'].vals = cameraPos; + + // Update view matrix + // roll, pitch and translate are all globals. + var xRotation = mat4.create(); + mat4.rotateY(xRotation, xRotation, roll); + var yRotation = mat4.create(); + mat4.rotateX(yRotation, yRotation, pitch); + this.viewMatrix = mat4.create(); + mat4.multiply(this.viewMatrix, yRotation, xRotation); + this.viewMatrix[14] = -translate; + var firstNode = this.nodes[0]; drawNodeRecursive(this, firstNode, mat4.create()); diff --git a/shaders/pbr-frag.glsl b/shaders/pbr-frag.glsl index 86b56ee2..1b9ff708 100644 --- a/shaders/pbr-frag.glsl +++ b/shaders/pbr-frag.glsl @@ -62,7 +62,8 @@ struct PBRInfo const float M_PI = 3.141592653589793; -// diffuse +// The following equations model the diffuse term of the lighting equation +// Implementation of diffuse from "Physically-Based Shading at Disney" by Brent Burley vec3 disneyDiffuse(PBRInfo pbrInputs) { float f90 = 2.*pbrInputs.LdotH*pbrInputs.LdotH*pbrInputs.roughness - 0.5; @@ -70,30 +71,34 @@ vec3 disneyDiffuse(PBRInfo pbrInputs) return (pbrInputs.baseColor/M_PI)*(1.0+f90*pow((1.0-pbrInputs.NdotL),5.0))*(1.0+f90*pow((1.0-pbrInputs.NdotV),5.0)); } +// basic Lambertian diffuse, implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog vec3 lambertianDiffuse(PBRInfo pbrInputs) { return pbrInputs.baseColor / M_PI; } -// F -// r +// The following equations model the Fresnel reflectance term of the spec equation (aka F()) +// implementation of fresnel from “An Inexpensive BRDF Model for Physically based Rendering” by Christophe Schlick vec3 fresnelSchlick2(PBRInfo pbrInputs) { return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); } +// Simplified implementation of fresnel from “An Inexpensive BRDF Model for Physically based Rendering” by Christophe Schlick vec3 fresnelSchlick(PBRInfo pbrInputs) { return pbrInputs.metalness + (vec3(1.0) - pbrInputs.metalness) * pow(1.0 - pbrInputs.VdotH, 5.0); } -// G -float microfacetCookTorrance(PBRInfo pbrInputs) +// The following equations model the geometric occlusion term of the spec equation (aka G()) +// Implementation from “A Reflectance Model for Computer Graphics” by Robert Cook and Kenneth Torrance, +float geometricOcclusionCookTorrance(PBRInfo pbrInputs) { return min(min(2.*pbrInputs.NdotV*pbrInputs.NdotH/pbrInputs.VdotH, 2.*pbrInputs.NdotL*pbrInputs.NdotH/pbrInputs.VdotH),1.0); } -float microfacetSchlick(PBRInfo pbrInputs) +// implementation of microfacet occlusion from “An Inexpensive BRDF Model for Physically based Rendering” by Christophe Schlick +float geometricOcclusionSchlick(PBRInfo pbrInputs) { float k = pbrInputs.roughness * 0.79788; // 0.79788 = sqrt(2.0/3.1415); // alternately, k can be defined with @@ -104,7 +109,8 @@ float microfacetSchlick(PBRInfo pbrInputs) return l * n; } -float microfacetSmith_var1(PBRInfo pbrInputs) +// the following Smith implementations are from “Geometrical Shadowing of a Random Rough Surface” by Bruce G. Smith +float geometricOcclusionSmith(PBRInfo pbrInputs) { float NdotL2 = pbrInputs.NdotL * pbrInputs.NdotL; float NdotV2 = pbrInputs.NdotV * pbrInputs.NdotV; @@ -113,21 +119,24 @@ float microfacetSmith_var1(PBRInfo pbrInputs) return (1. / max((1. + v + l ),0.000001)); } -float SmithVisibilityG1_var2(float NdotV, float r){ +float SmithG1_var2(float NdotV, float r) +{ float tanSquared = (1.0 - NdotV * NdotV) / max((NdotV * NdotV),0.00001); return 2.0 / (1.0 + sqrt(1.0 + r * r * tanSquared)); } -float SmithG1(float NdotV, float r) { +float SmithG1(float NdotV, float r) +{ return 2.0 * NdotV / (NdotV + sqrt(r*r+(1.0-r*r)*(NdotV*NdotV))); } - -float SmithVisibilityGGX(PBRInfo pbrInputs){ - return SmithVisibilityG1_var2(pbrInputs.NdotL, pbrInputs.roughness) * SmithVisibilityG1_var2(pbrInputs.NdotV, pbrInputs.roughness); +float geometricOcclusionSmithGGX(PBRInfo pbrInputs) +{ + return SmithG1_var2(pbrInputs.NdotL, pbrInputs.roughness) * SmithG1_var2(pbrInputs.NdotV, pbrInputs.roughness); } -// D +// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D()) +// implementation from “Average Irregularity Representation of a Roughened Surface for Ray Reflection” by T. S. Trowbridge, and K. P. Reitz float GGX(PBRInfo pbrInputs) { float roughnessSq = pbrInputs.roughness*pbrInputs.roughness; @@ -137,7 +146,6 @@ float GGX(PBRInfo pbrInputs) void main() { - // Normal Map #ifndef HAS_TANGENTS vec3 pos_dx = dFdx(v_Position); @@ -224,10 +232,10 @@ void main() { vec3 F = fresnelSchlick2(pbrInputs); //vec3 F = fresnelSchlick(pbrInputs); - //float G = microfacetCookTorrance(pbrInputs); - //float G = microfacetSmith(pbrInputs); - //float G = microfacetSchlick(pbrInputs); - float G = SmithVisibilityGGX(pbrInputs); + //float G = geometricOcclusionCookTorrance(pbrInputs); + //float G = geometricOcclusionSmith(pbrInputs); + //float G = geometricOcclusionSchlick(pbrInputs); + float G = geometricOcclusionSmithGGX(pbrInputs); float D = GGX(pbrInputs); vec3 diffuseContrib = (1.0 - F) * lambertianDiffuse(pbrInputs); diff --git a/shaders/pbr-vert.glsl b/shaders/pbr-vert.glsl index 344e73c9..b37d4820 100644 --- a/shaders/pbr-vert.glsl +++ b/shaders/pbr-vert.glsl @@ -10,7 +10,7 @@ attribute vec2 a_UV; #endif uniform mat4 u_mvpMatrix; -uniform mat4 u_NormalMatrix; +uniform mat4 u_modelMatrix; varying vec3 v_Position; varying vec2 v_UV; @@ -19,24 +19,24 @@ varying vec2 v_UV; #ifdef HAS_TANGENTS varying mat3 v_TBN; #else -varying vec3 v_Normal; +varying vec3 v_Normal; #endif #endif void main(){ - vec4 pos = u_NormalMatrix * a_Position; + vec4 pos = u_modelMatrix * a_Position; v_Position = vec3(pos.xyz) / pos.w; #ifdef HAS_NORMALS #ifdef HAS_TANGENTS - vec3 normalW = normalize(vec3(u_NormalMatrix * vec4(a_Normal.xyz, 0.0))); - vec3 tangentW = normalize(vec3(u_NormalMatrix * vec4(a_Tangent.xyz, 0.0))); + vec3 normalW = normalize(vec3(u_modelMatrix * vec4(a_Normal.xyz, 0.0))); + vec3 tangentW = normalize(vec3(u_modelMatrix * vec4(a_Tangent.xyz, 0.0))); vec3 bitangentW = cross(normalW, tangentW) * a_Tangent.w; v_TBN = mat3(tangentW, bitangentW, normalW); #else // HAS_TANGENTS != 1 - v_Normal = normalize(vec3(u_NormalMatrix * a_Normal)); + v_Normal = normalize(vec3(u_modelMatrix * a_Normal)); #endif #endif