投影查看定向光影映射的矩阵计算

时间:2015-03-28 08:04:40

标签: c++ opengl matrix directional-light

为了计算定向光的投影视图矩阵,我采用有源相机的截锥体的顶点,将它们乘以我的定向光的旋转,并使用这些旋转的顶点来计算正交投影矩阵的延伸为我的定向光。 然后我使用光的平截头体边界框的中心作为眼睛的位置,前向矢量的光线方向,然后是Y轴作为向上矢量来创建视图矩阵。

我通过将一个方框的8个角乘以2作为大小并以原点为中心来计算相机平截头体顶点。

一切正常,方向光投影视图矩阵是正确的,但我遇到了这个方法的一个大问题。

假设我的相机正面向前(0,0,-1),位于原点上,zNear值为1,zFar为100.只有从我的相机平截头体可见的物体才会被渲染到阴影贴图中,所以每个对象的Z位置都在-1到-100之间。

问题是,如果我的光线有一个方向使光线来自相机后面并且是一个物体,例如,Z位置为10(因此在相机​​后面但仍然在光线前)并且高到足以在我的相机可见的场景上投下阴影,这个物体不会被渲染到阴影贴图中,因为它没有包含在我的光锥体中,导致错误没有投射阴影。

为了解决这个问题,我正在考虑使用场景边界框来计算光投影视图矩阵,但这样做是没用的,因为渲染到阴影贴图中的图像很大,以至于可以看到许多工件(阴影痤疮等...),所以我跳过了这个解决方案。

我怎么能克服这个问题?


我在“计算紧密投影”部分下阅读this post来创建我的投影视图矩阵,为清楚起见,这是我的代码:

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 个答案:

没有答案