From b7e3e6db869a80e9a778c3d09b4939f3b280dd16 Mon Sep 17 00:00:00 2001 From: John Turner <7strbass@gmail.com> Date: Fri, 29 Sep 2023 14:47:05 -0400 Subject: [PATCH] --perform PBR calcs in ModelView (camera) space Removes unnecessary calculations separating and recombining Model and View matrices every draw. --- src/esp/gfx/Drawable.h | 25 +++------ src/esp/gfx/GenericDrawable.cpp | 10 +--- src/esp/gfx/PbrDrawable.cpp | 22 ++------ src/esp/gfx/PbrShader.cpp | 23 ++------- src/esp/gfx/PbrShader.h | 20 ++------ src/shaders/gfx/pbr.vert | 11 ++-- src/shaders/gfx/pbrIrradianceMap.frag | 74 +++++++++++++-------------- src/shaders/gfx/pbrLighting.glsl | 2 +- src/shaders/gfx/pbrMaterials.glsl | 10 ++-- src/shaders/gfx/pbrUniforms.glsl | 7 +-- 10 files changed, 68 insertions(+), 136 deletions(-) diff --git a/src/esp/gfx/Drawable.h b/src/esp/gfx/Drawable.h index e8ddb3430f..fb5a1b2085 100644 --- a/src/esp/gfx/Drawable.h +++ b/src/esp/gfx/Drawable.h @@ -219,20 +219,11 @@ class Drawable : public Magnum::SceneGraph::Drawable3D { * drawables draw function. * @param camera The camera passsed to this drawable's draw function * @param shader The shader this drawable consumes. - * @param getLightPosition The function to query for each light to acquire its - * proper position in the world. Flat/Phong objects query - * esp::gfx::getLightPositionRelativeToCamera(), while PBR objects query - * esp::gfx::getLightPositionRelativeToWorld() */ template - void updateShaderLightingParameters( - const Mn::Matrix4& transformationMatrix, - Mn::SceneGraph::Camera3D& camera, - ShaderType shader, - const std::function& - getLightPosition); + void updateShaderLightingParameters(const Mn::Matrix4& transformationMatrix, + Mn::SceneGraph::Camera3D& camera, + ShaderType shader); /** * @brief Drawable-specific update called at the end of @@ -265,11 +256,7 @@ template void Drawable::updateShaderLightingParameters( const Mn::Matrix4& transformationMatrix, Mn::SceneGraph::Camera3D& camera, - ShaderType shader, - const std::function& - getLightPosition) { + ShaderType shader) { const Mn::Matrix4 cameraMatrix = camera.cameraMatrix(); std::vector lightPositions; @@ -285,8 +272,8 @@ void Drawable::updateShaderLightingParameters( for (Mn::UnsignedInt i = 0; i < lightSetup_->size(); ++i) { const auto& lightInfo = (*lightSetup_)[i]; - Mn::Vector4 pos = - getLightPosition(lightInfo, transformationMatrix, cameraMatrix); + Mn::Vector4 pos = getLightPositionRelativeToCamera( + lightInfo, transformationMatrix, cameraMatrix); // flip directional lights to faciliate faster, non-forking calc in // shader. Leave non-directional lights unchanged pos *= (pos[3] * 2) - 1; diff --git a/src/esp/gfx/GenericDrawable.cpp b/src/esp/gfx/GenericDrawable.cpp index 255801772f..cff04048db 100644 --- a/src/esp/gfx/GenericDrawable.cpp +++ b/src/esp/gfx/GenericDrawable.cpp @@ -152,15 +152,7 @@ void GenericDrawable::draw(const Mn::Matrix4& transformationMatrix, updateShader(); // In Drawable.h - // Phong uses light position relative to camera - updateShaderLightingParameters(transformationMatrix, camera, shader_, - [](const LightInfo& lightInfo, - const Magnum::Matrix4& transformationMatrix, - const Magnum::Matrix4& cameraMatrix) { - return getLightPositionRelativeToCamera( - lightInfo, transformationMatrix, - cameraMatrix); - }); + updateShaderLightingParameters(transformationMatrix, camera, shader_); Mn::Matrix3x3 rotScale = transformationMatrix.rotationScaling(); // Find determinant to calculate backface culling winding dir diff --git a/src/esp/gfx/PbrDrawable.cpp b/src/esp/gfx/PbrDrawable.cpp index bc2bda3d31..9856f29965 100644 --- a/src/esp/gfx/PbrDrawable.cpp +++ b/src/esp/gfx/PbrDrawable.cpp @@ -441,16 +441,8 @@ void PbrDrawable::draw(const Mn::Matrix4& transformationMatrix, "PbrDrawable::draw() : GL mesh doesn't exist", ); updateShader(); - // In Drawable.h - // Pbr uses light position relative to world. - updateShaderLightingParameters(transformationMatrix, camera, shader_, - [](const LightInfo& lightInfo, - const Magnum::Matrix4& transformationMatrix, - const Magnum::Matrix4& cameraMatrix) { - return getLightPositionRelativeToWorld( - lightInfo, transformationMatrix, - cameraMatrix); - }); + // In Drawable.h. + updateShaderLightingParameters(transformationMatrix, camera, shader_); // ABOUT PbrShader::Flag::DoubleSided: // @@ -465,10 +457,7 @@ void PbrDrawable::draw(const Mn::Matrix4& transformationMatrix, Mn::GL::Renderer::disable(Mn::GL::Renderer::Feature::FaceCulling); } - Mn::Matrix4 modelMatrix = - camera.cameraMatrix().inverted() * transformationMatrix; - - Mn::Matrix3x3 rotScale = modelMatrix.rotationScaling(); + Mn::Matrix3x3 rotScale = transformationMatrix.rotationScaling(); // Find determinant to calculate backface culling winding dir const float normalDet = rotScale.determinant(); // Normal matrix is calculated as `m.inverted().transposed()`, and @@ -495,11 +484,8 @@ void PbrDrawable::draw(const Mn::Matrix4& transformationMatrix, ? 0 : node_.getSemanticId())) .setProjectionMatrix(camera.projectionMatrix()) - .setViewMatrix(camera.cameraMatrix()) - .setModelMatrix(modelMatrix) // NOT modelview matrix! + .setModelViewMatrix(transformationMatrix) .setNormalMatrix(normalMatrix) - .setCameraWorldPosition( - camera.object().absoluteTransformationMatrix().translation()) .setBaseColor(matCache.baseColor) .setRoughness(matCache.roughness) .setMetallic(matCache.metalness) diff --git a/src/esp/gfx/PbrShader.cpp b/src/esp/gfx/PbrShader.cpp index 0ab1ba191f..00f7baf756 100644 --- a/src/esp/gfx/PbrShader.cpp +++ b/src/esp/gfx/PbrShader.cpp @@ -273,8 +273,7 @@ PbrShader::PbrShader(Flags originalFlags, } // cache the uniform locations - viewMatrixUniform_ = uniformLocation("uViewMatrix"); - modelMatrixUniform_ = uniformLocation("uModelMatrix"); + modelViewMatrixUniform_ = uniformLocation("uModelViewMatrix"); normalMatrixUniform_ = uniformLocation("uNormalMatrix"); projMatrixUniform_ = uniformLocation("uProjectionMatrix"); @@ -350,8 +349,6 @@ PbrShader::PbrShader(Flags originalFlags, directLightingIntensityUniform_ = uniformLocation("uDirectLightIntensity"); } - cameraWorldPosUniform_ = uniformLocation("uCameraWorldPos"); - if ((directLightingIsEnabled_ && flags_ >= Flag::UseDirectLightTonemap) || (flags_ >= Flag::ImageBasedLighting && flags_ >= Flag::UseIBLTonemap)) { tonemapExposureUniform_ = uniformLocation("uExposure"); @@ -387,8 +384,7 @@ PbrShader::PbrShader(Flags originalFlags, // Initializations // initialize the shader with some "reasonable defaults" - setViewMatrix(Mn::Matrix4{Mn::Math::IdentityInit}); - setModelMatrix(Mn::Matrix4{Mn::Math::IdentityInit}); + setModelViewMatrix(Mn::Matrix4{Mn::Math::IdentityInit}); setProjectionMatrix(Mn::Matrix4{Mn::Math::IdentityInit}); if (lightingIsEnabled_) { setBaseColor(Mn::Color4{0.7f}); @@ -616,13 +612,8 @@ PbrShader& PbrShader::setNormalMatrix(const Mn::Matrix3x3& matrix) { return *this; } -PbrShader& PbrShader::setViewMatrix(const Mn::Matrix4& matrix) { - setUniform(viewMatrixUniform_, matrix); - return *this; -} - -PbrShader& PbrShader::setModelMatrix(const Mn::Matrix4& matrix) { - setUniform(modelMatrixUniform_, matrix); +PbrShader& PbrShader::setModelViewMatrix(const Mn::Matrix4& matrix) { + setUniform(modelViewMatrixUniform_, matrix); return *this; } @@ -743,12 +734,6 @@ PbrShader& PbrShader::setDebugDisplay(PbrDebugDisplay index) { return *this; } -PbrShader& PbrShader::setCameraWorldPosition( - const Mn::Vector3& cameraWorldPos) { - setUniform(cameraWorldPosUniform_, cameraWorldPos); - return *this; -} - PbrShader& PbrShader::setTextureMatrix(const Mn::Matrix3& matrix) { CORRADE_ASSERT(flags_ >= Flag::TextureTransformation, "PbrShader::setTextureMatrix(): the shader was not " diff --git a/src/esp/gfx/PbrShader.h b/src/esp/gfx/PbrShader.h index 80902226bf..b62cd5ddca 100644 --- a/src/esp/gfx/PbrShader.h +++ b/src/esp/gfx/PbrShader.h @@ -482,16 +482,10 @@ class PbrShader : public Magnum::GL::AbstractShaderProgram { PbrShader& setProjectionMatrix(const Magnum::Matrix4& matrix); /** - * @brief Set view matrix to the uniform on GPU + * @brief Set model-view matrix to the uniform on GPU * @return Reference to self (for method chaining) */ - PbrShader& setViewMatrix(const Magnum::Matrix4& matrix); - - /** - * @brief Set model matrix to the uniform on GPU - * @return Reference to self (for method chaining) - */ - PbrShader& setModelMatrix(const Magnum::Matrix4& matrix); + PbrShader& setModelViewMatrix(const Magnum::Matrix4& matrix); /** * @brief Set normal matrix to the uniform on GPU @@ -578,12 +572,6 @@ class PbrShader : public Magnum::GL::AbstractShaderProgram { */ PbrShader& setObjectId(unsigned int objectId); - /** - * @brief Set object id to the uniform on GPU - * @return Reference to self (for method chaining) - */ - PbrShader& setCameraWorldPosition(const Magnum::Vector3& cameraWorldPos); - /** * @brief Set total mipmap levels of the prefiltered environment map to the * uniform on GPU @@ -803,8 +791,7 @@ class PbrShader : public Magnum::GL::AbstractShaderProgram { // it hurts the performance to call glGetUniformLocation() every frame due // to string operations. therefore, cache the locations in the constructor // material uniforms - int viewMatrixUniform_ = ID_UNDEFINED; - int modelMatrixUniform_ = ID_UNDEFINED; + int modelViewMatrixUniform_ = ID_UNDEFINED; int normalMatrixUniform_ = ID_UNDEFINED; int projMatrixUniform_ = ID_UNDEFINED; int baseColorUniform_ = ID_UNDEFINED; // diffuse color @@ -834,7 +821,6 @@ class PbrShader : public Magnum::GL::AbstractShaderProgram { // invGamma value for linear->sRGB mapping approx int invGammaUniform_ = ID_UNDEFINED; - int cameraWorldPosUniform_ = ID_UNDEFINED; int prefilteredMapMipLevelsUniform_ = ID_UNDEFINED; // Clearcoat layer diff --git a/src/shaders/gfx/pbr.vert b/src/shaders/gfx/pbr.vert index 01b6a71776..b9bfb36ac5 100644 --- a/src/shaders/gfx/pbr.vert +++ b/src/shaders/gfx/pbr.vert @@ -35,16 +35,15 @@ out highp vec3 biTangent; #endif // ------------ uniform ---------------------- -uniform highp mat4 uViewMatrix; uniform highp mat3 uNormalMatrix; // inverse transpose of 3x3 model matrix, NOT // modelview matrix -uniform highp mat4 uModelMatrix; +uniform highp mat4 uModelViewMatrix; uniform highp mat4 uProjectionMatrix; // ------------ shader ----------------------- void main() { - vec4 vertexWorldPosition = uModelMatrix * vertexPosition; - position = vertexWorldPosition.xyz; + vec4 vertexWorldPosition = uModelViewMatrix * vertexPosition; + position = vertexWorldPosition.xyz / vertexWorldPosition.w; normal = normalize(uNormalMatrix * vertexNormal); #if defined(TEXTURED) texCoord = @@ -61,9 +60,9 @@ void main() { tangent = normalize(tangent - dot(tangent, normal) * normal); biTangent = normalize(cross(normal, tangent) * vertexTangent.w); // later in .frag, TBN will transform the normal perturbation - // (read from normal map) from tangent space to world space, + // (read from normal map) from tangent space to camera space, // NOT camera space #endif - gl_Position = uProjectionMatrix * uViewMatrix * vertexWorldPosition; + gl_Position = uProjectionMatrix * vertexWorldPosition; } diff --git a/src/shaders/gfx/pbrIrradianceMap.frag b/src/shaders/gfx/pbrIrradianceMap.frag index d0aa8a20aa..591387fc99 100644 --- a/src/shaders/gfx/pbrIrradianceMap.frag +++ b/src/shaders/gfx/pbrIrradianceMap.frag @@ -5,7 +5,7 @@ precision highp float; // ------------ input ------------------------ -in highp vec4 position; // world position +in highp vec4 position; // world position // ------------ uniform ---------------------- uniform samplerCube EnvironmentMap; @@ -14,39 +14,39 @@ uniform samplerCube EnvironmentMap; layout(location = OUTPUT_ATTRIBUTE_LOCATION_COLOR) out highp vec4 fragmentColor; // -------------- shader --------------------- - void main() { - vec3 normal = normalize(position.xyz); // N (z axis) - vec3 up = vec3(0.0, 1.0, 0.0); // U (y axis) - vec3 right = normalize(cross(up, normal)); // R (x axis) - up = normalize(cross(normal, right)); - - vec3 irradiance = vec3(0.0); - const uint sampleCounts = 4096u; - for (uint iPoint = 0u; iPoint < sampleCounts; ++iPoint) { - // sample point on 2D plane - // check pbrCommon.glsl for mor details - vec2 xi = hammersley2d(iPoint, sampleCounts); - - // spherical to cartesian (in tangent space) z-up - // z-up (not y-up) is fine here - // check pbrCommon.glsl for mor details - vec3 tangentSample = hemisphereSample_cos(xi.x, xi.y); - - // tangent space to world: [R U N][tangentSample] = xR + yU + zN - // we make the normal dirction in world space aligned with z-axis in tangent space - vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal; - - // Careful: - // We used cosinus mapping not uniform mapping. - // We generated proportionally fewer rays at the center top of the hemisphere. - // So there is no need to compensate for the smaller areas - // by scaling the area by sinTheta - // irradiance += textureLod(EnvironmentMap, sampleVec, 0.0).rgb; - // Dev note: - // if the irradianceMap is full black, - // check if Environment enables mipmaps and you do populate them correctly. - irradiance += texture(EnvironmentMap, sampleVec).rgb; - } - - fragmentColor = vec4(irradiance / float(sampleCounts), 1.0); - } +void main() { + vec3 normal = normalize(position.xyz); // N (z axis) + vec3 up = vec3(0.0, 1.0, 0.0); // U (y axis) + vec3 right = normalize(cross(up, normal)); // R (x axis) + up = normalize(cross(normal, right)); + + vec3 irradiance = vec3(0.0); + const uint sampleCounts = 4096u; + for (uint iPoint = 0u; iPoint < sampleCounts; ++iPoint) { + // sample point on 2D plane + // check pbrCommon.glsl for mor details + vec2 xi = hammersley2d(iPoint, sampleCounts); + + // spherical to cartesian (in tangent space) z-up + // z-up (not y-up) is fine here + // check pbrCommon.glsl for mor details + vec3 tangentSample = hemisphereSample_cos(xi.x, xi.y); + + // tangent space to world: [R U N][tangentSample] = xR + yU + zN + // we make the normal dirction in world space aligned with z-axis in tangent + // space + vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + + tangentSample.z * normal; + + // Careful: + // We used cosinus mapping not uniform mapping. + // We generated proportionally fewer rays at the center top of the + // hemisphere. So there is no need to compensate for the smaller areas by + // scaling the area by sinTheta irradiance += textureLod(EnvironmentMap, + // sampleVec, 0.0).rgb; Dev note: if the irradianceMap is full black, check + // if Environment enables mipmaps and you do populate them correctly. + irradiance += texture(EnvironmentMap, sampleVec).rgb; + } + + fragmentColor = vec4(irradiance / float(sampleCounts), 1.0); +} diff --git a/src/shaders/gfx/pbrLighting.glsl b/src/shaders/gfx/pbrLighting.glsl index 894c924e8f..ed9fdee762 100644 --- a/src/shaders/gfx/pbrLighting.glsl +++ b/src/shaders/gfx/pbrLighting.glsl @@ -193,7 +193,7 @@ vec4 calcFinalIrradiance(vec4 IBLIrradiance) { } // calcFinalIrradiance // diffuseColor: diffuse color -// n: normal on shading location in world space +// n: normal on shading location in camera space vec3 computeIBLDiffuse(vec3 diffuseColor, vec3 n) { // diffuse part = diffuseColor * irradiance // Query the IBL diffuse irradiance from the appropriate cubemap diff --git a/src/shaders/gfx/pbrMaterials.glsl b/src/shaders/gfx/pbrMaterials.glsl index 24e35c069e..a3cacfb6b8 100644 --- a/src/shaders/gfx/pbrMaterials.glsl +++ b/src/shaders/gfx/pbrMaterials.glsl @@ -81,14 +81,14 @@ mat3 buildTBN() { // Derive normal from normal map sample // normTextureSample : Normal texture sample at texCoords // normTextureScale : scale amount for normal texture sample -// TBN : Tangent/Bitangent/Normal frame, maps tangentspace to world +// TBN : Tangent/Bitangent/Normal frame, maps tangentspace to camera space vec3 getNormalFromNormalMap(vec3 normTextureSample, float normalTextureScale, mat3 TBN) { vec3 tangentNormal = normalize((normTextureSample * 2.0 - 1.0) * vec3(normalTextureScale, normalTextureScale, 1.0)); - // TBN transforms tangentNormal from tangent space to world space + // TBN transforms tangentNormal from tangent space to camera space return normalize(TBN * tangentNormal); } #endif // if (defined(NORMAL_TEXTURE)|| defined(CLEAR_COAT_NORMAL_TEXTURE) || @@ -140,9 +140,9 @@ PBRData buildPBRData() { 1.0f; #endif // if defined(CLEAR_COAT) else - // View is the normalized vector from the shading location to the camera - // in *world space* - pbrInfo.view = normalize(uCameraWorldPos - position); + // View is the normalized vector from the shading location to the origin (in + // camera space) + pbrInfo.view = normalize(-position); // cos angle between view and normal // clamp to ensure range adherence, abs instead of epsilon to avoid errors at diff --git a/src/shaders/gfx/pbrUniforms.glsl b/src/shaders/gfx/pbrUniforms.glsl index 9096b7cc87..6f651187dc 100644 --- a/src/shaders/gfx/pbrUniforms.glsl +++ b/src/shaders/gfx/pbrUniforms.glsl @@ -5,7 +5,7 @@ precision highp float; // -------------- input --------------------- -// position, normal, tangent, biTangent in world space, +// position, normal, tangent, biTangent in camera space, // NOT camera space in highp vec3 position; in highp vec3 normal; @@ -22,9 +22,6 @@ in highp vec3 biTangent; uniform highp uint uObjectId; #endif -// camera position in world space -uniform highp vec3 uCameraWorldPos; - #if defined(PBR_DEBUG_DISPLAY) uniform int uPbrDebugDisplay; #endif @@ -135,7 +132,7 @@ uniform vec3 uInvGamma; uniform vec3 uLightColors[LIGHT_COUNT]; uniform float uLightRanges[LIGHT_COUNT]; -// lights in world space! +// lights in camera space // if .w == 0, it means it is a directional light, .xyz is the direction; // if .w == 1, it means it is a point light, .xyz is the light position; // it is NOT put in the Light Structure, simply because we may modify the code