【Overload游戏引擎分析】从视图投影矩阵提取视锥体及overload对视锥体的封装

news/2024/7/7 21:40:07

overoad代码中包含一段有意思的代码,可以从视图投影矩阵逆推出摄像机的视锥体,本文来分析一下原理

一、平面的方程

平面方程可以由法线N=(A, B, C)和一个点Q=(x0,y0,z0)定义,其形式为:
A ( x − x 0 ) + B ( y − y 0 ) + C ( z − z 0 ) = 0 A(x-x_{0})+B(y-y_{0})+C(z-z_{0})=0 A(xx0)+B(yy0)+C(zz0)=0          整理变为: A x + B y + C z + D = 0 Ax+By+Cz+D=0 Ax+By+Cz+D=0,       其中 D = − A x 0 − B y 0 − C z 0 D=−Ax_{0}−By_{0}−Cz_{0} D=Ax0By0Cz0
         方程进一步可以将方程归一化:
A A 2 + B 2 + C 2 x + B A 2 + B 2 + C 2 y + C A 2 + B 2 + C 2 z + D A 2 + B 2 + C 2 = 0 \frac{A}{\sqrt{A^{2}+B^{2}+C^{2} } } x + \frac{B}{\sqrt{A^{2}+B^{2}+C^{2} } }y+\frac{C}{\sqrt{A^{2}+B^{2}+C^{2} } }z+\frac{D}{\sqrt{A^{2}+B^{2}+C^{2} } } = 0 A2+B2+C2 Ax+A2+B2+C2 By+A2+B2+C2 Cz+A2+B2+C2 D=0 写成通用格式 a x + b y + c z + d = 0 ax+by+cz+d=0 ax+by+cz+d=0
那么点 p = ( x 1 , y 1 , z 1 ) p=(x_{1}, y_{1}, z_{1}) p=(x1,y1,z1)到平面的距离为:
D = a x 1 + b y 1 + c z 1 + d D=ax_{1}+by_{1}+cz_{1}+d D=ax1+by1+cz1+d
一个平面会将空间分成两个半空间(halfspace),进一步法线的朝向的空间称为正半空间(positive halfspace),法线背离的空间称为反半空间(negative halfspace)。根据D的符号可以判断点的相对位置:

  • D < 0, 点位于反半空间
  • D = 0, 点位于平面上
  • D > 0, 点位于正半空间

这种特性可用于判断点是否在视锥体内部。

二、OpenGL视锥体

视锥体是摄像机能看到的区域,只有在视锥体内的物体才能被看到。其由近平面、远平面与周围四个面组成,形成一个平截头体区域。
在这里插入图片描述

三、Overload对视锥体的封装

Overload对视锥体的封装在文件Frustum.h、Frustum.cpp中。先看其定义:

class Frustum
{
public:
	/**
	* 根据视图投影矩阵提取视锥体
	* @param p_viewProjection
	*/ 
	void CalculateFrustum(const OvMaths::FMatrix4& _viewProjection);
	/**
	* 判断点是不是在视锥体内
	* @param p_x
	* @param p_y
	* @param p_z
	*/
	bool PointInFrustum(float p_x, float p_y, float _z) const;
	/**
	* 判断球是不是在视锥体内
	* @param p_x
	* @param p_y
	* @param p_z
	* @param p_radius
	*/
	bool SphereInFrustum(float p_x, float p_y, loat p_z, float p_radius) const;
	/**
	* 判断立方体是不是在视锥体内
	* @param p_x
	* @param p_y
	* @param p_z
	* @param p_size
	*/
	bool CubeInFrustum(float p_x, float p_y, float _z, float p_size) const;
	/**
	* 判断包围球是不是在视锥体内
	* @param p_boundingSphere
	* @param p_transform
	*/
	bool BoundingSphereInFrustum(const vRendering::Geometry::BoundingSphere& _boundingSphere, const OvMaths::FTransform& _transform) const;
	/**
	* 返回近平面
	*/
	std::array<float, 4> GetNearPlane() const;
	/**
	* 返回远平面
	*/
	std::array<float, 4> GetFarPlane() const;
private:
	float m_frustum[6][4];  // 6个平面的方程参数
};

m_frustum保存着6个平面的方程参数,为了提升操作便利性,其定义了两个枚举作为索引:

enum FrustumSide
{
	RIGHT = 0,		// The RIGHT side of the frustum
	LEFT = 1,		// The LEFT	 side of the frustum
	BOTTOM = 2,		// The BOTTOM side of the frustum
	TOP = 3,		// The TOP side of the frustum
	BACK = 4,		// The BACK	side of the frustum
	FRONT = 5		// The FRONT side of the frustum
};

// 平面方程的参数索引
enum PlaneData
{
	A = 0,				// The X value of the plane's normal
	B = 1,				// The Y value of the plane's normal
	C = 2,				// The Z value of the plane's normal
	D = 3				// The distance the plane is from the origin
};

函数的具体实现在文件Frustum.cpp中,我们先看最基础的判断点是否在视锥体内:

bool OvRendering::Data::Frustum::PointInFrustum(float x, float y, float z) const
{
	for (int i = 0; i < 6; i++)
	{
		if (m_frustum[i][A] * x + m_frustum[i][B] * y + m_frustum[i][C] * z + m_frustum[i][D] <= 0)
		{
			return false;
		}
	}
	return true;
}

定义视锥体的面法线都是朝外的,所有点的面的距离必须全部小于0。进一步判断球体是否完全在视锥体内,距离必须小于半径的负数。
最后分析一下CalculateFrustum,它是根据一个视图投影矩阵反向构建一个视锥体,具体公式怎么来的可以参考这篇文章,里面将的特别详细:
https://www.gamedevs.org/uploads/fast-extraction-viewing-frustum-planes-from-world-view-projection-matrix.pdf


http://lihuaxi.xjx100.cn/news/1598762.html

相关文章

ERDAS 2022 安装教程

注意&#xff1a; 演示ERDAS版本为&#xff1a;2022.v16.7.0.1216 安装程序&#xff1a; 1、主程序&#xff1a;点击下载 2、许可文件&#xff1a;点击下载 3、IDM下载器&#xff1a;点击下载 下载速度&#xff1a; 浏览器下载速度慢&#xff0c;可以使用以上提供的IDM下…

JavaEE初阶学习:HTTP协议和Tomcat

1. HTTP协议 HTTP协议是一个非常广泛的应用层协议~~ 应用层协议 —> TCP IP 协议栈 应用层 —> 关注数据怎么使用~ 传输层 —> 关注的是整个传输的起点和终点 网络层 —> 地址管理 路由选择 数据链路层 —> 相邻节点之间的数据转发 物理层 —> 基础设置,硬…

JavaEE-文件IO操作

构造方法 一般方法&#xff0c;有很多&#xff0c;我们以下只是列举几个经常使用的 注意在上述的操作过程中&#xff0c;无论是绝对路径下的这个文件还是相对路径下的这个文件&#xff0c;都是不存在的 Reader 使用 --> 文本文件 FileReader类所涉及到的一些方法 Fil…

RF元素定位

元素定位方式&#xff1a;id, name, link, partial_link_text, xpath, css id 【登录输入框】id session_email_or_mobile_number input text id session_email_or_mobile_numbername 【登录输入框】name session[email_or_mobile_number] input text name sessi…

SAP CDS 基础知识

CDS 入门知识&#xff1a;https://zhuanlan.zhihu.com/p/610924866?utm_id0 CDS 基本语法&#xff1a;https://blog.csdn.net/XLevon/article/details/128669506 测试CDS发布的oDATA:https://blogs.sap.com/2016/03/10/my-cds-view-self-study-tutorial-part-1-how-to-test-od…

Jmeter常用断言之断言持续时间简介

Duration Assertion&#xff1a;断言持续时间。 断言持续时间通常用于做性能测试&#xff0c;一般用于检查HTTP请求的响应时间是否超过预期值。而这个响应时间是性能测试中常关注的一个性能指标。 一、添加断言方式 根据需要可在【测试计划】、【线程组】、【线程请求】下添加…

代码随想录二刷day49

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣121. 买卖股票的最佳时机二、力扣122. 买卖股票的最佳时机 II 前言 一、力扣121. 买卖股票的最佳时机 lass Solution {public int maxProfit(int[] pr…

数据结构 | (三) Stack

栈 &#xff1a;一种特殊的线性表&#xff0c;其 只允许在固定的一端进行插入和删除元素操作 。 进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO &#xff08; Last In First Out &#xff09;的原则。 压栈&#xff1a;栈…