从视图和投影矩阵中获取它们时出现错误的平截头平面
Wrong frustum plane when getting them from view and proj matrix
我在使用 aabb 进行视锥体剔除时遇到了一些麻烦。所有的平截头体平面似乎都是错误的,我不知道它来自哪里。
这是计算平截头体平面的代码。
std::vector<math::Vec4> BaseCamera::GetFrustumPlanes()
{
//Matrix are Column Major
math::Matrix4 mat = viewMatrix_ * projectionMatrix_;
std::vector<math::Vec4> tempFrustumPlane(6);
// Left Frustum Plane
tempFrustumPlane[0] = mat.GetColumn(3) + mat.GetColumn(0);
// Right Frustum Plane
// Subtract first column of matrix from the fourth column
tempFrustumPlane[1] = mat.GetColumn(3) - mat.GetColumn(0);
// Top Frustum Plane
tempFrustumPlane[2] = mat.GetColumn(3) - mat.GetColumn(1);
// Bottom Frustum Plane
tempFrustumPlane[3] = mat.GetColumn(3) + mat.GetColumn(1);
// Near Frustum Plane
tempFrustumPlane[4] = mat.GetColumn(3) - mat.GetColumn(2);
// Far Frustum Plane
// Subtract third column of matrix from the fourth column
tempFrustumPlane[5] = mat.GetColumn(3) + mat.GetColumn(2);
// Normalize plane normals (A, B and C (xyz))
for(int i = 0; i < 6; i++) {
const auto length = sqrt((tempFrustumPlane[i].x * tempFrustumPlane[i].x) + (tempFrustumPlane[i].y * tempFrustumPlane[i].y) + (tempFrustumPlane[i].z * tempFrustumPlane[i].z));
tempFrustumPlane[i].x /= length;
tempFrustumPlane[i].y /= length;
tempFrustumPlane[i].z /= length;
tempFrustumPlane[i].w /= length;
}
return tempFrustumPlane;
}
viewMatrix 是使用此函数计算的
Matrix4 Matrix4::LookAt(const Vec3 eye, const Vec3 center, const Vec3 up)
{
const Vec3 f((center - eye).Normalize());
const Vec3 s(Vec3::Cross(f, up).Normalize());
const Vec3 u(Vec3::Cross(s, f));
Matrix4 result = Identity();
result[0][0] = s.x;
result[1][0] = s.y;
result[2][0] = s.z;
result[0][1] = u.x;
result[1][1] = u.y;
result[2][1] = u.z;
result[0][2] = -f.x;
result[1][2] = -f.y;
result[2][2] = -f.z;
result[3][0] = -(s * eye);
result[3][1] = -(u * eye);
result[3][2] = (f * eye);
return result;
}
以及使用此函数计算的视角
Matrix4 Matrix4::Perspective(
const float fov,
const float aspect,
const float near,
const float far)
{
const float tanHalfFov = tan(fov * 0.5f);
Matrix4 result(0);
result[0][0] = 1.0f / (aspect * tanHalfFov);
result[1][1] = 1.0f / tanHalfFov;
result[2][2] = far / (near - far);
result[2][3] = -1.0f;
result[3][2] = -(far * near) / (far - near);
return result;
}
最后我在这个函数中针对截锥体测试 aabbs
bool DrawSystem::CullAABB(
const physics::AABB aabb,
std::vector<math::Vec4> frustumPlanes)
{
const float minX = aabb.centerPoint.x - aabb.extent.x;
const float maxX = aabb.centerPoint.x + aabb.extent.x;
const float minY = aabb.centerPoint.y - aabb.extent.y;
const float maxY = aabb.centerPoint.y + aabb.extent.y;
const float minZ = aabb.centerPoint.z - aabb.extent.z;
const float maxZ = aabb.centerPoint.z + aabb.extent.z;
auto& gizmoCommandBuffer = GraphicsEngine::Get().GetGizmoCommandBuffer();
math::Vec3 center = Camera::Get().GetPosition() + Camera::Get().GetFront() * 2;
// check box outside/inside of frustum
for (int i = 0; i < 6; i++)
{
math::Vec3 dir{ frustumPlanes[i].x, frustumPlanes[i].y, frustumPlanes[i].z };
if(i == 0) { //left
gizmoCommandBuffer.SetColor(Color::red);
} else if(i == 1) { //right
gizmoCommandBuffer.SetColor(Color::yellow);
}
else if (i == 2) { //top
gizmoCommandBuffer.SetColor(Color::blue);
}
else if (i == 3) { //bottom
gizmoCommandBuffer.SetColor(Color::green);
}
else {
gizmoCommandBuffer.SetColor(Color::grey);
}
for(int j = 0; j < 10; j++) {
gizmoCommandBuffer.DrawWireSphere(center + (dir * j / 10 )* 0.1f, 0.01f);
}
if (frustumPlanes[i] * math::Vec4(minX, minY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, minY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(minX, maxY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, maxY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(minX, minY, maxZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, minY, maxZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(minX, maxY, maxZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, maxY, maxZ, 1.0f) > 0.0f) {
continue;
}
return false;
}
return true;
}
在引擎中我做了一个测试。 aabbs 被绘制(蓝色考虑在相机外面,红色在里面):
十字好像还行,但我一转动相机,十字就错了。
AABB 正常工作,因为它们绘制正确。
如果您还需要什么就问
我在这个 repos 中找到了一个可行的解决方案:https://github.com/EQMG/Acid/blob/master/Sources/Physics/Frustum.cpp
我在使用 aabb 进行视锥体剔除时遇到了一些麻烦。所有的平截头体平面似乎都是错误的,我不知道它来自哪里。
这是计算平截头体平面的代码。
std::vector<math::Vec4> BaseCamera::GetFrustumPlanes()
{
//Matrix are Column Major
math::Matrix4 mat = viewMatrix_ * projectionMatrix_;
std::vector<math::Vec4> tempFrustumPlane(6);
// Left Frustum Plane
tempFrustumPlane[0] = mat.GetColumn(3) + mat.GetColumn(0);
// Right Frustum Plane
// Subtract first column of matrix from the fourth column
tempFrustumPlane[1] = mat.GetColumn(3) - mat.GetColumn(0);
// Top Frustum Plane
tempFrustumPlane[2] = mat.GetColumn(3) - mat.GetColumn(1);
// Bottom Frustum Plane
tempFrustumPlane[3] = mat.GetColumn(3) + mat.GetColumn(1);
// Near Frustum Plane
tempFrustumPlane[4] = mat.GetColumn(3) - mat.GetColumn(2);
// Far Frustum Plane
// Subtract third column of matrix from the fourth column
tempFrustumPlane[5] = mat.GetColumn(3) + mat.GetColumn(2);
// Normalize plane normals (A, B and C (xyz))
for(int i = 0; i < 6; i++) {
const auto length = sqrt((tempFrustumPlane[i].x * tempFrustumPlane[i].x) + (tempFrustumPlane[i].y * tempFrustumPlane[i].y) + (tempFrustumPlane[i].z * tempFrustumPlane[i].z));
tempFrustumPlane[i].x /= length;
tempFrustumPlane[i].y /= length;
tempFrustumPlane[i].z /= length;
tempFrustumPlane[i].w /= length;
}
return tempFrustumPlane;
}
viewMatrix 是使用此函数计算的
Matrix4 Matrix4::LookAt(const Vec3 eye, const Vec3 center, const Vec3 up)
{
const Vec3 f((center - eye).Normalize());
const Vec3 s(Vec3::Cross(f, up).Normalize());
const Vec3 u(Vec3::Cross(s, f));
Matrix4 result = Identity();
result[0][0] = s.x;
result[1][0] = s.y;
result[2][0] = s.z;
result[0][1] = u.x;
result[1][1] = u.y;
result[2][1] = u.z;
result[0][2] = -f.x;
result[1][2] = -f.y;
result[2][2] = -f.z;
result[3][0] = -(s * eye);
result[3][1] = -(u * eye);
result[3][2] = (f * eye);
return result;
}
以及使用此函数计算的视角
Matrix4 Matrix4::Perspective(
const float fov,
const float aspect,
const float near,
const float far)
{
const float tanHalfFov = tan(fov * 0.5f);
Matrix4 result(0);
result[0][0] = 1.0f / (aspect * tanHalfFov);
result[1][1] = 1.0f / tanHalfFov;
result[2][2] = far / (near - far);
result[2][3] = -1.0f;
result[3][2] = -(far * near) / (far - near);
return result;
}
最后我在这个函数中针对截锥体测试 aabbs
bool DrawSystem::CullAABB(
const physics::AABB aabb,
std::vector<math::Vec4> frustumPlanes)
{
const float minX = aabb.centerPoint.x - aabb.extent.x;
const float maxX = aabb.centerPoint.x + aabb.extent.x;
const float minY = aabb.centerPoint.y - aabb.extent.y;
const float maxY = aabb.centerPoint.y + aabb.extent.y;
const float minZ = aabb.centerPoint.z - aabb.extent.z;
const float maxZ = aabb.centerPoint.z + aabb.extent.z;
auto& gizmoCommandBuffer = GraphicsEngine::Get().GetGizmoCommandBuffer();
math::Vec3 center = Camera::Get().GetPosition() + Camera::Get().GetFront() * 2;
// check box outside/inside of frustum
for (int i = 0; i < 6; i++)
{
math::Vec3 dir{ frustumPlanes[i].x, frustumPlanes[i].y, frustumPlanes[i].z };
if(i == 0) { //left
gizmoCommandBuffer.SetColor(Color::red);
} else if(i == 1) { //right
gizmoCommandBuffer.SetColor(Color::yellow);
}
else if (i == 2) { //top
gizmoCommandBuffer.SetColor(Color::blue);
}
else if (i == 3) { //bottom
gizmoCommandBuffer.SetColor(Color::green);
}
else {
gizmoCommandBuffer.SetColor(Color::grey);
}
for(int j = 0; j < 10; j++) {
gizmoCommandBuffer.DrawWireSphere(center + (dir * j / 10 )* 0.1f, 0.01f);
}
if (frustumPlanes[i] * math::Vec4(minX, minY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, minY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(minX, maxY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, maxY, minZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(minX, minY, maxZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, minY, maxZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(minX, maxY, maxZ, 1.0f) > 0.0f) {
continue;
}
if (frustumPlanes[i] * math::Vec4(maxX, maxY, maxZ, 1.0f) > 0.0f) {
continue;
}
return false;
}
return true;
}
在引擎中我做了一个测试。 aabbs 被绘制(蓝色考虑在相机外面,红色在里面):
十字好像还行,但我一转动相机,十字就错了。
AABB 正常工作,因为它们绘制正确。
如果您还需要什么就问
我在这个 repos 中找到了一个可行的解决方案:https://github.com/EQMG/Acid/blob/master/Sources/Physics/Frustum.cpp