Skip to content

Commit

Permalink
--perform PBR calcs in ModelView (camera) space
Browse files Browse the repository at this point in the history
Removes unnecessary calculations separating and recombining Model and View matrices every draw.
  • Loading branch information
jturner65 committed Oct 2, 2023
1 parent 4dabed9 commit b7e3e6d
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 136 deletions.
25 changes: 6 additions & 19 deletions src/esp/gfx/Drawable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class ShaderType>
void updateShaderLightingParameters(
const Mn::Matrix4& transformationMatrix,
Mn::SceneGraph::Camera3D& camera,
ShaderType shader,
const std::function<Magnum::Vector4(const LightInfo&,
const Magnum::Matrix4&,
const Magnum::Matrix4&)>&
getLightPosition);
void updateShaderLightingParameters(const Mn::Matrix4& transformationMatrix,
Mn::SceneGraph::Camera3D& camera,
ShaderType shader);

/**
* @brief Drawable-specific update called at the end of
Expand Down Expand Up @@ -265,11 +256,7 @@ template <class ShaderType>
void Drawable::updateShaderLightingParameters(
const Mn::Matrix4& transformationMatrix,
Mn::SceneGraph::Camera3D& camera,
ShaderType shader,
const std::function<Magnum::Vector4(const LightInfo&,
const Magnum::Matrix4&,
const Magnum::Matrix4&)>&
getLightPosition) {
ShaderType shader) {
const Mn::Matrix4 cameraMatrix = camera.cameraMatrix();

std::vector<Mn::Vector4> lightPositions;
Expand All @@ -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;
Expand Down
10 changes: 1 addition & 9 deletions src/esp/gfx/GenericDrawable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 4 additions & 18 deletions src/esp/gfx/PbrDrawable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
//
Expand All @@ -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
Expand All @@ -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)
Expand Down
23 changes: 4 additions & 19 deletions src/esp/gfx/PbrShader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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});
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 "
Expand Down
20 changes: 3 additions & 17 deletions src/esp/gfx/PbrShader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 5 additions & 6 deletions src/shaders/gfx/pbr.vert
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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;
}
74 changes: 37 additions & 37 deletions src/shaders/gfx/pbrIrradianceMap.frag
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
precision highp float;

// ------------ input ------------------------
in highp vec4 position; // world position
in highp vec4 position; // world position

// ------------ uniform ----------------------
uniform samplerCube EnvironmentMap;
Expand All @@ -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);
}
2 changes: 1 addition & 1 deletion src/shaders/gfx/pbrLighting.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions src/shaders/gfx/pbrMaterials.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -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) ||
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit b7e3e6d

Please sign in to comment.