better normal transforms
We use the cofactors to transform normals, which preserves its direction, unlike of the inverse-transpose method.
This commit is contained in:
committed by
Mathias Agopian
parent
10259f80f4
commit
f728776714
@@ -69,7 +69,7 @@ void FScene::prepare(const mat4f& worldOriginTransform) {
|
||||
|
||||
size_t renderableDataCapacity = entities.size();
|
||||
// we need the capacity to be multiple of 16 for SIMD loops
|
||||
renderableDataCapacity = (renderableDataCapacity + 0xF) & ~0xF;
|
||||
renderableDataCapacity = (renderableDataCapacity + 0xFu) & ~0xFu;
|
||||
// we need 1 extra entry at the end for the summed primitive count
|
||||
renderableDataCapacity = renderableDataCapacity + 1;
|
||||
|
||||
@@ -82,7 +82,7 @@ void FScene::prepare(const mat4f& worldOriginTransform) {
|
||||
// dominating directional light, even if there are no entities.
|
||||
size_t lightDataCapacity = std::max<size_t>(1, entities.size());
|
||||
// we need the capacity to be multiple of 16 for SIMD loops
|
||||
lightDataCapacity = (lightDataCapacity + 0xF) & ~0xF;
|
||||
lightDataCapacity = (lightDataCapacity + 0xFu) & ~0xFu;
|
||||
|
||||
lightData.clear();
|
||||
if (lightData.capacity() < lightDataCapacity) {
|
||||
@@ -137,8 +137,8 @@ void FScene::prepare(const mat4f& worldOriginTransform) {
|
||||
if (lcm.getIntensity(li) >= maxIntensity) {
|
||||
maxIntensity = lcm.getIntensity(li);
|
||||
float3 d = lcm.getLocalDirection(li);
|
||||
// using the inverse-transpose handles non-uniform scaling
|
||||
d = normalize(transpose(inverse(worldTransform.upperLeft())) * d);
|
||||
// using mat3f::getTransformForNormals handles non-uniform scaling
|
||||
d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d);
|
||||
lightData.elementAt<FScene::POSITION_RADIUS>(0) = float4{ 0, 0, 0, std::numeric_limits<float>::infinity() };
|
||||
lightData.elementAt<FScene::DIRECTION>(0) = d;
|
||||
lightData.elementAt<FScene::LIGHT_INSTANCE>(0) = li;
|
||||
@@ -148,8 +148,8 @@ void FScene::prepare(const mat4f& worldOriginTransform) {
|
||||
float3 d = 0;
|
||||
if (!lcm.isPointLight(li) || lcm.isIESLight(li)) {
|
||||
d = lcm.getLocalDirection(li);
|
||||
// using the inverse-transpose handles non-uniform scaling
|
||||
d = normalize(transpose(inverse(worldTransform.upperLeft())) * d);
|
||||
// using mat3f::getTransformForNormals handles non-uniform scaling
|
||||
d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d);
|
||||
}
|
||||
lightData.push_back_unsafe(
|
||||
float4{ p.xyz, lcm.getRadius(li) }, d, li, {}, {});
|
||||
@@ -160,7 +160,7 @@ void FScene::prepare(const mat4f& worldOriginTransform) {
|
||||
// some elements past the end of the array will be accessed by SIMD code, we need to make
|
||||
// sure the data is valid enough as not to produce errors such as divide-by-zero
|
||||
// (e.g. in computeLightRanges())
|
||||
for (size_t i = lightData.size(), e = (lightData.size() + 3) & ~3; i < e; i++) {
|
||||
for (size_t i = lightData.size(), e = (lightData.size() + 3u) & ~3u; i < e; i++) {
|
||||
new(lightData.data<POSITION_RADIUS>() + i) float4{ 0, 0, 0, 1 };
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Hand
|
||||
offset + offsetof(PerRenderableUib, worldFromModelMatrix),
|
||||
model);
|
||||
|
||||
// Using the inverse-transpose handles non-uniform scaling, but DOESN'T guarantee that
|
||||
// Using mat3f::getTransformForNormals handles non-uniform scaling, but DOESN'T guarantee that
|
||||
// the transformed normals will have unit-length, therefore they need to be normalized
|
||||
// in the shader (that's already the case anyways, since normalization is needed after
|
||||
// interpolation).
|
||||
@@ -192,7 +192,7 @@ void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Hand
|
||||
//
|
||||
// Note: if the model matrix is known to be a rigid-transform, we could just use it directly.
|
||||
|
||||
mat3f m = transpose(inverse(model.upperLeft()));
|
||||
mat3f m = mat3f::getTransformForNormals(model.upperLeft());
|
||||
m *= mat3f(1.0f / std::sqrt(max(float3{length2(m[0]), length2(m[1]), length2(m[2])})));
|
||||
|
||||
UniformBuffer::setUniform(buffer,
|
||||
|
||||
@@ -238,15 +238,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, size_t O>
|
||||
constexpr inline T minor(Matrix<T, O> src, size_t row, size_t col);
|
||||
|
||||
template<typename T, size_t O>
|
||||
struct Determinant {
|
||||
static constexpr T determinant(Matrix<T, O> in) {
|
||||
T det = {};
|
||||
for (size_t i = 0; i < O; i++) {
|
||||
T m = minor<T, O>(in, 0, i);
|
||||
T m = Determinant<T, O - 1>::determinant(Matrix<T, O>::submatrix(in, 0, i));
|
||||
T factor = (i % 2 == 1) ? T(-1) : T(1);
|
||||
det += factor * in[0][i] * m;
|
||||
}
|
||||
@@ -266,11 +263,6 @@ struct Determinant<T, 1> {
|
||||
static constexpr T determinant(Matrix<T, 1> in) { return in[0][0]; }
|
||||
};
|
||||
|
||||
template<typename T, size_t O>
|
||||
constexpr inline T minor(Matrix<T, O> src, size_t row, size_t col) {
|
||||
return Determinant<T, O - 1>::determinant(Matrix<T, O>::submatrix(src, row, col));
|
||||
}
|
||||
|
||||
template<typename MATRIX>
|
||||
constexpr MATRIX MATH_PURE cofactor(const MATRIX& m) {
|
||||
typedef typename MATRIX::value_type T;
|
||||
@@ -288,7 +280,8 @@ constexpr MATRIX MATH_PURE cofactor(const MATRIX& m) {
|
||||
for (size_t i = 0; i < order; i++) {
|
||||
for (size_t j = 0; j < order; j++) {
|
||||
T factor = ((i + j) % 2 == 1) ? T(-1) : T(1);
|
||||
out[i][j] = minor<T, order>(in, i, j) * factor;
|
||||
out[i][j] = Determinant<T, order - 1>::determinant(
|
||||
Matrix<T, order>::submatrix(in, i, j)) * factor;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -259,6 +259,18 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matrix suitable for transforming normals
|
||||
*
|
||||
* @param m the transform applied to vertices
|
||||
* @return a matrix to apply to normals
|
||||
*
|
||||
* @warning normals transformed by this matrix must be normalized
|
||||
*/
|
||||
static constexpr TMat33 getTransformForNormals(const TMat33& m) noexcept {
|
||||
return matrix::cof(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs the tangent frame represented by the specified matrix into a quaternion.
|
||||
* Reflection is preserved by encoding it as the sign of the w component in the
|
||||
|
||||
Reference in New Issue
Block a user