Вычисление матрицы проекционного вида для картографирования теней направленного света

Чтобы вычислить матрицу проекционного вида для направленного света, я беру вершины усеченного контура моей активной камеры, умножаю их на вращение моего направленного света и использую эти повернутые вершины, чтобы вычислить протяженность матрицы ортографической проекции для моего направленного света. свет. Затем я создаю матрицу вида, используя центр ограничительной рамки моего источника света в качестве положения глаза, направление света для вектора вперед, а затем ось Y в качестве вектора вверх.

Я вычисляю вершины усеченной камеры, умножая 8 углов поля на 2 в качестве размера и центрируя в начале координат.

Все работает нормально, и матрица вида проекции светового потока верна, но с этим методом я столкнулся с большой проблемой.

Предположим, что моя камера направлена ​​вперед (0, 0, -1), расположена в исходной точке и имеет значение zNear, равное 1, и значение zFar, равное 100. В карту теней отображаются только объекты, видимые из области моей камеры, поэтому каждый объект который имеет позицию Z между -1 и -100.

Проблема в том, что если мой свет имеет направление, которое заставляет свет исходить из-за камеры и является объектом, например, с позицией Z 10 (так за камерой, но все еще перед светом) и достаточно высоким чтобы возможно отбрасывать тень на сцену, видимую из моей камеры, этот объект не отображается на карте теней, потому что он не включен в мою область света, что приводит к ошибке, не отбрасывающей тень.

Чтобы решить эту проблему, я подумывал об использовании ограничивающей рамки сцены для вычисления матрицы проецирования света, но делать это было бы бесполезно, потому что изображение, отображаемое в карту теней, было настолько большим, что были бы видны многочисленные артефакты (угри тени и т.д...), поэтому я пропустил это решение.

Как я мог преодолеть эту проблему?


Я прочитал этот пост в разделе "Расчет точной проекции", чтобы создать свою матрицу проекционного вида, и, для ясности, это мой код:

Frustum* cameraFrustum = activeCamera->GetFrustum();

Vertex3f direction = GetDirection();                                        // z axis
Vertex3f perpVec1 = (direction ^ Vertex3f(0.0f, 0.0f, 1.0f)).Normalized();  // y axis
Vertex3f perpVec2 = (direction ^ perpVec1).Normalized();                    // x axis

Matrix rotationMatrix;  
rotationMatrix.m[0] = perpVec2.x;   rotationMatrix.m[1] = perpVec1.x;   rotationMatrix.m[2] =   direction.x;
rotationMatrix.m[4] = perpVec2.y;   rotationMatrix.m[5] = perpVec1.y;   rotationMatrix.m[6] =   direction.y;
rotationMatrix.m[8] = perpVec2.z;   rotationMatrix.m[9] = perpVec1.z;   rotationMatrix.m[10] =  direction.z;

Vertex3f frustumVertices[8];
cameraFrustum->GetFrustumVertices(frustumVertices);

for (AInt i = 0; i < 8; i++)
    frustumVertices[i] = rotationMatrix * frustumVertices[i];

Vertex3f minV = frustumVertices[0], maxV = frustumVertices[0];
for (AInt i = 1; i < 8; i++)
{
    minV.x = min(minV.x, frustumVertices[i].x);
    minV.y = min(minV.y, frustumVertices[i].y);
    minV.z = min(minV.z, frustumVertices[i].z);
    maxV.x = max(maxV.x, frustumVertices[i].x);
    maxV.y = max(maxV.y, frustumVertices[i].y);
    maxV.z = max(maxV.z, frustumVertices[i].z);
}

Vertex3f extends = maxV - minV;
extends *= 0.5f;

Matrix viewMatrix = Matrix::MakeLookAt(cameraFrustum->GetBoundingBoxCenter(), direction, perpVec1);
Matrix projectionMatrix = Matrix::MakeOrtho(-extends.x, extends.x, -extends.y, extends.y, -extends.z, extends.z);
Matrix projectionViewMatrix = projectionMatrix * viewMatrix;

SceneObject::SetMatrix("ViewMatrix", viewMatrix);
SceneObject::SetMatrix("ProjectionMatrix", projectionMatrix);
SceneObject::SetMatrix("ProjectionViewMatrix", projectionViewMatrix);

И вот как я вычисляю усеченную фигуру и ее ограничивающую рамку:

Matrix inverseProjectionViewMatrix = projectionViewMatrix.Inversed();

Vertex3f points[8];

_frustumVertices[0] = inverseProjectionViewMatrix * Vertex3f(-1.0f,  1.0f, -1.0f);  // near top-left
_frustumVertices[1] = inverseProjectionViewMatrix * Vertex3f( 1.0f,  1.0f, -1.0f);  // near top-right
_frustumVertices[2] = inverseProjectionViewMatrix * Vertex3f(-1.0f, -1.0f, -1.0f);  // near bottom-left
_frustumVertices[3] = inverseProjectionViewMatrix * Vertex3f( 1.0f, -1.0f, -1.0f);  // near bottom-right
_frustumVertices[4] = inverseProjectionViewMatrix * Vertex3f(-1.0f,  1.0f,  1.0f);  // far top-left
_frustumVertices[5] = inverseProjectionViewMatrix * Vertex3f( 1.0f,  1.0f,  1.0f);  // far top-right
_frustumVertices[6] = inverseProjectionViewMatrix * Vertex3f(-1.0f, -1.0f,  1.0f);  // far bottom-left
_frustumVertices[7] = inverseProjectionViewMatrix * Vertex3f( 1.0f, -1.0f,  1.0f);  // far bottom-right

_boundingBoxMin = _frustumVertices[0];
_boundingBoxMax = _frustumVertices[0];

for (AInt i = 1; i < 8; i++)
{
    _boundingBoxMin.x = min(_boundingBoxMin.x, _frustumVertices[i].x);
    _boundingBoxMin.y = min(_boundingBoxMin.y, _frustumVertices[i].y);
    _boundingBoxMin.z = min(_boundingBoxMin.z, _frustumVertices[i].z);

    _boundingBoxMax.x = max(_boundingBoxMax.x, _frustumVertices[i].x);
    _boundingBoxMax.y = max(_boundingBoxMax.y, _frustumVertices[i].y);
    _boundingBoxMax.z = max(_boundingBoxMax.z, _frustumVertices[i].z);
}

_boundingBoxCenter = Vertex3f((_boundingBoxMin.x + _boundingBoxMax.x) / 2.0f, (_boundingBoxMin.y + _boundingBoxMax.y) / 2.0f, (_boundingBoxMin.z + _boundingBoxMax.z) / 2.0f);

0 ответов

Другие вопросы по тегам