Implements virtual shadow mapping for EEVEE-Next primary shadow solution. This technique aims to deliver really high precision shadowing for many lights while keeping a relatively low cost. The technique works by splitting each shadows in tiles that are only allocated & updated on demand by visible surfaces and volumes. Local lights use cubemap projection with mipmap level of detail to adapt the resolution to the receiver distance. Sun lights use clipmap distribution or cascade distribution (depending on which is better) for selecting the level of detail with the distance to the camera. Current maximum shadow precision for local light is about 1 pixel per 0.01 degrees. For sun light, the maximum resolution is based on the camera far clip distance which sets the most coarse clipmap. ## Limitation: Alpha Blended surfaces might not get correct shadowing in some corner casses. This is to be fixed in another commit. While resolution is greatly increase, it is still finite. It is virtually equivalent to one 8K shadow per shadow cube face and per clipmap level. There is no filtering present for now. ## Parameters: Shadow Pool Size: In bytes, amount of GPU memory to dedicate to the shadow pool (is allocated per viewport). Shadow Scaling: Scale the shadow resolution. Base resolution should target subpixel accuracy (within the limitation of the technique). Related to #93220 Related to #104472
569 lines
16 KiB
C++
569 lines
16 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2022 Blender Foundation. */
|
|
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include <cmath>
|
|
#include <type_traits>
|
|
|
|
#include "BLI_math_base.hh"
|
|
#include "BLI_math_vector_types.hh"
|
|
#include "BLI_span.hh"
|
|
#include "BLI_utildefines.h"
|
|
|
|
namespace blender::math {
|
|
|
|
/**
|
|
* Returns true if all components are exactly equal to 0.
|
|
*/
|
|
template<typename T, int Size> [[nodiscard]] inline bool is_zero(const VecBase<T, Size> &a)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
if (a[i] != T(0)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns true if at least one component is exactly equal to 0.
|
|
*/
|
|
template<typename T, int Size> [[nodiscard]] inline bool is_any_zero(const VecBase<T, Size> &a)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
if (a[i] == T(0)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the given vectors are equal within the given epsilon.
|
|
* The epsilon is scaled for each component by magnitude of the matching component of `a`.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline bool almost_equal_relative(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b,
|
|
const T &epsilon_factor)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
const float epsilon = epsilon_factor * math::abs(a[i]);
|
|
if (math::distance(a[i], b[i]) > epsilon) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename T, int Size> [[nodiscard]] inline VecBase<T, Size> abs(const VecBase<T, Size> &a)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = a[i] >= 0 ? a[i] : -a[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns -1 if \a a is less than 0, 0 if \a a is equal to 0, and +1 if \a a is greater than 0.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> sign(const VecBase<T, Size> &a)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = math::sign(a[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> min(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = a[i] < b[i] ? a[i] : b[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> max(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = a[i] > b[i] ? a[i] : b[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> clamp(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &min,
|
|
const VecBase<T, Size> &max)
|
|
{
|
|
VecBase<T, Size> result = a;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = std::clamp(result[i], min[i], max[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> clamp(const VecBase<T, Size> &a, const T &min, const T &max)
|
|
{
|
|
VecBase<T, Size> result = a;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = std::clamp(result[i], min, max);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> mod(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
BLI_assert(b[i] != 0);
|
|
result[i] = std::fmod(a[i], b[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> mod(const VecBase<T, Size> &a, const T &b)
|
|
{
|
|
BLI_assert(b != 0);
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = std::fmod(a[i], b);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Safe version of mod(a, b) that returns 0 if b is equal to 0.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> safe_mod(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = (b[i] != 0) ? std::fmod(a[i], b[i]) : 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Safe version of mod(a, b) that returns 0 if b is equal to 0.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> safe_mod(const VecBase<T, Size> &a, const T &b)
|
|
{
|
|
if (b == 0) {
|
|
return VecBase<T, Size>(0);
|
|
}
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = std::fmod(a[i], b);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns \a a if it is a multiple of \a b or the next multiple or \a b after \b a .
|
|
* In other words, it is equivalent to `divide_ceil(a, b) * b`.
|
|
* It is undefined if \a a is negative or \b b is not strictly positive.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> ceil_to_multiple(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
BLI_assert(a[i] >= 0);
|
|
BLI_assert(b[i] > 0);
|
|
result[i] = ((a[i] + b[i] - 1) / b[i]) * b[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Integer division that returns the ceiling, instead of flooring like normal C division.
|
|
* It is undefined if \a a is negative or \b b is not strictly positive.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> divide_ceil(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
BLI_assert(a[i] >= 0);
|
|
BLI_assert(b[i] > 0);
|
|
result[i] = (a[i] + b[i] - 1) / b[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
void min_max(const VecBase<T, Size> &vector, VecBase<T, Size> &min, VecBase<T, Size> &max)
|
|
{
|
|
min = math::min(vector, min);
|
|
max = math::max(vector, max);
|
|
}
|
|
|
|
/**
|
|
* Returns 0 if denominator is exactly equal to 0.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> safe_divide(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = (b[i] == 0) ? 0 : a[i] / b[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns 0 if denominator is exactly equal to 0.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> safe_divide(const VecBase<T, Size> &a, const T &b)
|
|
{
|
|
return (b != 0) ? a / b : VecBase<T, Size>(0.0f);
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> floor(const VecBase<T, Size> &a)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = std::floor(a[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> round(const VecBase<T, Size> &a)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = std::round(a[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> ceil(const VecBase<T, Size> &a)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = std::ceil(a[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> fract(const VecBase<T, Size> &a)
|
|
{
|
|
VecBase<T, Size> result;
|
|
for (int i = 0; i < Size; i++) {
|
|
result[i] = a[i] - std::floor(a[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Dot product between two vectors.
|
|
* Equivalent to component wise multiplication followed by summation of the result.
|
|
* Equivalent to the cosine of the angle between the two vectors if the vectors are normalized.
|
|
* \note prefer using `length_manhattan(a)` than `dot(a, vec(1))` to get the sum of all components.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline T dot(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
|
{
|
|
T result = a[0] * b[0];
|
|
for (int i = 1; i < Size; i++) {
|
|
result += a[i] * b[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns summation of all components.
|
|
*/
|
|
template<typename T, int Size> [[nodiscard]] inline T length_manhattan(const VecBase<T, Size> &a)
|
|
{
|
|
T result = std::abs(a[0]);
|
|
for (int i = 1; i < Size; i++) {
|
|
result += std::abs(a[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T, int Size> [[nodiscard]] inline T length_squared(const VecBase<T, Size> &a)
|
|
{
|
|
return dot(a, a);
|
|
}
|
|
|
|
template<typename T, int Size> [[nodiscard]] inline T length(const VecBase<T, Size> &a)
|
|
{
|
|
return std::sqrt(length_squared(a));
|
|
}
|
|
|
|
/** Return true if each individual column is unit scaled. Mainly for assert usage. */
|
|
template<typename T, int Size> [[nodiscard]] inline bool is_unit_scale(const VecBase<T, Size> &v)
|
|
{
|
|
/* Checks are flipped so NAN doesn't assert because we're making sure the value was
|
|
* normalized and in the case we don't want NAN to be raising asserts since there
|
|
* is nothing to be done in that case. */
|
|
const T test_unit = math::length_squared(v);
|
|
return (!(std::abs(test_unit - T(1)) >= AssertUnitEpsilon<T>::value) ||
|
|
!(std::abs(test_unit) >= AssertUnitEpsilon<T>::value));
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline T distance_manhattan(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
|
{
|
|
return length_manhattan(a - b);
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline T distance_squared(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
|
{
|
|
return length_squared(a - b);
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline T distance(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
|
{
|
|
return length(a - b);
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> reflect(const VecBase<T, Size> &incident,
|
|
const VecBase<T, Size> &normal)
|
|
{
|
|
BLI_assert(is_unit_scale(normal));
|
|
return incident - 2.0 * dot(normal, incident) * normal;
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> refract(const VecBase<T, Size> &incident,
|
|
const VecBase<T, Size> &normal,
|
|
const T &eta)
|
|
{
|
|
float dot_ni = dot(normal, incident);
|
|
float k = 1.0f - eta * eta * (1.0f - dot_ni * dot_ni);
|
|
if (k < 0.0f) {
|
|
return VecBase<T, Size>(0.0f);
|
|
}
|
|
return eta * incident - (eta * dot_ni + sqrt(k)) * normal;
|
|
}
|
|
|
|
/**
|
|
* Project \a p onto \a v_proj .
|
|
* Returns zero vector if \a v_proj is degenerate (zero length).
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> project(const VecBase<T, Size> &p,
|
|
const VecBase<T, Size> &v_proj)
|
|
{
|
|
if (UNLIKELY(is_zero(v_proj))) {
|
|
return VecBase<T, Size>(0.0f);
|
|
}
|
|
return v_proj * (dot(p, v_proj) / dot(v_proj, v_proj));
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> normalize_and_get_length(const VecBase<T, Size> &v,
|
|
T &out_length)
|
|
{
|
|
out_length = length_squared(v);
|
|
/* A larger value causes normalize errors in a scaled down models with camera extreme close. */
|
|
constexpr T threshold = std::is_same_v<T, double> ? 1.0e-70 : 1.0e-35f;
|
|
if (out_length > threshold) {
|
|
out_length = sqrt(out_length);
|
|
return v / out_length;
|
|
}
|
|
/* Either the vector is small or one of it's values contained `nan`. */
|
|
out_length = 0.0;
|
|
return VecBase<T, Size>(0.0);
|
|
}
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> normalize(const VecBase<T, Size> &v)
|
|
{
|
|
T len;
|
|
return normalize_and_get_length(v, len);
|
|
}
|
|
|
|
/**
|
|
* \return cross perpendicular vector to \a a and \a b.
|
|
* \note Return zero vector if \a a and \a b are collinear.
|
|
* \note The length of the resulting vector is equal to twice the area of the triangle between \a a
|
|
* and \a b ; and it is equal to the sine of the angle between \a a and \a b if they are
|
|
* normalized.
|
|
* \note Blender 3D space uses right handedness: \a a = thumb, \a b = index, return = middle.
|
|
*/
|
|
template<typename T>
|
|
[[nodiscard]] inline VecBase<T, 3> cross(const VecBase<T, 3> &a, const VecBase<T, 3> &b)
|
|
{
|
|
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
|
}
|
|
|
|
/**
|
|
* Same as `cross(a, b)` but use double float precision for the computation.
|
|
*/
|
|
[[nodiscard]] inline VecBase<float, 3> cross_high_precision(const VecBase<float, 3> &a,
|
|
const VecBase<float, 3> &b)
|
|
{
|
|
return {float(double(a.y) * double(b.z) - double(a.z) * double(b.y)),
|
|
float(double(a.z) * double(b.x) - double(a.x) * double(b.z)),
|
|
float(double(a.x) * double(b.y) - double(a.y) * double(b.x))};
|
|
}
|
|
|
|
/**
|
|
* \param poly: Array of points around a polygon. They don't have to be co-planar.
|
|
* \return Best fit plane normal for the given polygon loop or zero vector if point
|
|
* array is too short. Not normalized.
|
|
*/
|
|
template<typename T> [[nodiscard]] inline VecBase<T, 3> cross_poly(Span<VecBase<T, 3>> poly)
|
|
{
|
|
/* Newell's Method. */
|
|
int nv = int(poly.size());
|
|
if (nv < 3) {
|
|
return VecBase<T, 3>(0, 0, 0);
|
|
}
|
|
const VecBase<T, 3> *v_prev = &poly[nv - 1];
|
|
const VecBase<T, 3> *v_curr = &poly[0];
|
|
VecBase<T, 3> n(0, 0, 0);
|
|
for (int i = 0; i < nv;) {
|
|
n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]);
|
|
n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]);
|
|
n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]);
|
|
v_prev = v_curr;
|
|
++i;
|
|
if (i < nv) {
|
|
v_curr = &poly[i];
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* Per component linear interpolation.
|
|
* \param t: interpolation factor. Return \a a if equal 0. Return \a b if equal 1.
|
|
* Outside of [0..1] range, use linear extrapolation.
|
|
*/
|
|
template<typename T, typename FactorT, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> interpolate(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b,
|
|
const FactorT &t)
|
|
{
|
|
return a * (1 - t) + b * t;
|
|
}
|
|
|
|
/**
|
|
* \return Point halfway between \a a and \a b.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> midpoint(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b)
|
|
{
|
|
return (a + b) * 0.5;
|
|
}
|
|
|
|
/**
|
|
* Return `vector` if `incident` and `reference` are pointing in the same direction.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline VecBase<T, Size> faceforward(const VecBase<T, Size> &vector,
|
|
const VecBase<T, Size> &incident,
|
|
const VecBase<T, Size> &reference)
|
|
{
|
|
return (dot(reference, incident) < 0) ? vector : -vector;
|
|
}
|
|
|
|
/**
|
|
* \return Index of the component with the greatest magnitude.
|
|
*/
|
|
template<typename T> [[nodiscard]] inline int dominant_axis(const VecBase<T, 3> &a)
|
|
{
|
|
VecBase<T, 3> b = abs(a);
|
|
return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2));
|
|
}
|
|
|
|
/**
|
|
* Calculates a perpendicular vector to \a v.
|
|
* \note Returned vector can be in any perpendicular direction.
|
|
* \note Returned vector might not the same length as \a v.
|
|
*/
|
|
template<typename T> [[nodiscard]] inline VecBase<T, 3> orthogonal(const VecBase<T, 3> &v)
|
|
{
|
|
const int axis = dominant_axis(v);
|
|
switch (axis) {
|
|
case 0:
|
|
return {-v.y - v.z, v.x, v.x};
|
|
case 1:
|
|
return {v.y, -v.x - v.z, v.y};
|
|
case 2:
|
|
return {v.z, v.z, -v.x - v.y};
|
|
}
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Calculates a perpendicular vector to \a v.
|
|
* \note Returned vector can be in any perpendicular direction.
|
|
*/
|
|
template<typename T> [[nodiscard]] inline VecBase<T, 2> orthogonal(const VecBase<T, 2> &v)
|
|
{
|
|
return {-v.y, v.x};
|
|
}
|
|
|
|
/**
|
|
* Returns true if vectors are equal within the given epsilon.
|
|
*/
|
|
template<typename T, int Size>
|
|
[[nodiscard]] inline bool is_equal(const VecBase<T, Size> &a,
|
|
const VecBase<T, Size> &b,
|
|
const T epsilon = T(0))
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
if (std::abs(a[i] - b[i]) > epsilon) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** Intersections. */
|
|
|
|
template<typename T> struct isect_result {
|
|
enum {
|
|
LINE_LINE_COLINEAR = -1,
|
|
LINE_LINE_NONE = 0,
|
|
LINE_LINE_EXACT = 1,
|
|
LINE_LINE_CROSS = 2,
|
|
} kind;
|
|
typename T::base_type lambda;
|
|
};
|
|
|
|
template<typename T, int Size>
|
|
[[nodiscard]] isect_result<VecBase<T, Size>> isect_seg_seg(const VecBase<T, Size> &v1,
|
|
const VecBase<T, Size> &v2,
|
|
const VecBase<T, Size> &v3,
|
|
const VecBase<T, Size> &v4);
|
|
|
|
} // namespace blender::math
|