对于类似这样的问题:“判断一个世界空间中的点是否在视野范围内,是否能被看见?”、“一个点(位置)是否能被某NPC的视线看到?”,这里我给出一个非常简单的做法, 不用在世界空间与视锥体进行任何数学计算(不用任何点、面、射线。。。),接下来的例子中大家会看到该方法是多么难以置信的简单。
为了验证,我们需要现在场景中放一个cube,来模拟一个点,尽量缩小一点,并在属性中记下它的位置(x,y,z)坐标。
然后创建一个材质,起名叫printPos,用来可视化坐标信息。
进入材质编辑,主要用到TransformToClipSpace节点(转换一个世界坐标到裁剪空间),DebugFloat2Value2(打印一个Float2向量的值),
如图连接后,在左侧的材质预览窗口应该可以看到一个红绿显示的两行数字,分别代表一个二维向量的x,y的值。
这个值就是我们给定的世界坐标变换到裁剪空间后的坐标,我们回到主场景中,为了看到坐标值,我们要再拖一个cube到场景中,把printPos材质赋给这个cube。
这时候,你可以移动视角,先让那个较小的cube(以下称为模拟点)出现在视野中,注意观察一下坐标值,当模拟点位于视野正中心时,坐标值为(0,0);
当模拟点在屏幕x方向超出视野时,红色数字的绝对值>1;
当模拟点在屏幕y方向超出视野时,绿色数字的绝对值>1。
这就是我们判断的理论依据了,当世界坐标被变换到裁剪空间时(此时的坐标为归一化透视坐标),x,y在区间(-1,1)内,当x或y的绝对值超过1时该坐标不在视锥体内,即不可被看见。稍作扩展,如果我们将这个点变换到另一个View(例如一个怪物或NPC)的裁剪空间,就可以判断该点是否能被怪物(NPC)看到。这里我就不展开讨论如何获取指定NPC的View了,在蓝图或C++代码中应该可以很方便获取到。
当然,本文仅提供一个方法,即我们不用在世界空间中拿这个点来判断是否在视锥体内,这个数学计算通常很复杂,需要视锥体的六个面方程,简化的计算通常也导致结果不精确,而使用本文的方法即将坐标转换到裁剪空间再进行比较,结果非常精确(这也是管线进行视锥体剔除的方法),实现步骤也很简单(一次空间转换,一次比较),大家有兴趣可以实践一下。
注:DebugFloat2Values节点还有对应的几个DebugFloat3Values,DebugFloat4Values方法是很有用的调试功能,可以用来可视化一些不容易看到的数值。
补充:评论中有人提到如果是正背对着目标的时候,有一段也是在-1~1这个范围的,这的确是一个没有考虑到的问题。
原因是由于我们使用TransformToClipSpace函数返回的"Clip Space XY"没有给我们提供z坐标,也就是说我们丢失了z坐标信息,这个z坐标是指View空间的forward方向的值,导致我们没有信息判断目标是否在视线后方,那怎么办呢?其实z值是可以得到的:将TransformToClipSpace节点使用新建Custom节点替代,
Custom Code中写入mul(float4(In.xyz,1),View.WorldToClip)就可以返回完整的齐次坐标(x,y,z,w),之前的Clip Space X,Y在这就是x/w,y/w,而z值就是w分量(注意,z值已被存储到w分量,而不是z分量),通过判断这个w的符号就可以知道目标在前方还是后方。
|