3D对象鼠标像素拾取解决方案

        标题的功能是公司以前项目中的新加的功能,之前的鼠标物体拾取是通过cpu计算射线和物体AABB相交来判断,但这种方式存在很大弊端,如果物体成长条形,那么在某些角度下AABB就比物体大的多,再者像一些镂空很多的物体,如蜘蛛、栅栏等,会遮挡后面物体的拾取,要能够精确的判断的话,就需要做射线和模型三角网格的相交检测,由于可选择的物体可能很多,模型也可能会很复杂,所以在没有简化网格的情况下这个计算量可能非常大,耗时长。讨论解决办法时同事建议在像素级别来做这个操作,于是眼前一亮,想了下觉得可行,然后下来经过几番思考,最后结合项目得到如下解决方案,可以作为3D游戏拾取方案的参考。

       在涉及具体方案前先说下几个项目涉及到的问题

       1、物体拾取时有类型优先级,比如:npc要优先选择,如果npc被周围玩家完全遮挡,那么npc要优先能被选中。

       2、拾取逻辑和后续事件处理逻辑的协调。

       3、效率及优化


先说下像素拾取方法,原理很简单,通过额为的渲染将物体的指针或者id号作为颜色传给pixel shader输出到texture,cpu回读这个texture,然后通过获取像素点颜色值,确定被选中的物体。

        完整的解决方案分如下几步:

1、视锥体裁剪不可见物体(一般渲染步骤会包含这个操作)

2、按物体类型,进行射线和AABB相交,进一步裁剪,并记录哪些物体和类型需要做拾取判断

3、按类型进行拾取渲染,每种类型用一个RTT,跳过不需要拾取的类型的渲染

4、回读需要判断类型的texture到内存,按类型优先级判断是否有物体被拾取到,如果有选中则可以跳过后续类型的判断,没有则继续判断下一类型

5、如果最终有选中物体,则进行鼠标事件处理

上述步骤中,第五步涉及到前面提到的问题2和3,原因是:按照一般的帧循环逻辑,是先framemove后render,而像素拾取必须要render后才能得到结果。当然等像素拾取后再处理鼠标事件也可行,但是这样会让cpu和gpu相互等待,破环工作的异步性,更优化的办法是把获取结果和鼠标事件处理放到下一帧的开始去做。根据具体情况,可能还需要和UI鼠标事件协调,如果有UI鼠标事件,则可以忽略结果获取和处理。

        在整个解决方案中还有个非常需要优化的地方,就是texture的回读,因为cpu回读gpu数据是一个非常耗时的操作,所以进行了两个处理:

1、为了提高cpu和gpu并行性,每种有拾取判断类型的texture都会完整回读到内存,而不是直接到gpu中去取像素点的值,同时为了减少回读数据,每个texture大小为3x3的9个像素(理论上1个像素点就够了,但觉得有点不踏实)。

2、为了只记录以鼠标点为中心的9个像素值,追加了一次2D的二次投影。


总的来说像素拾取可以很精确的的进行拾取操作,对帧率的影响也很小,经测试项目以70帧的话会有1、2帧的影响,当然像素拾取也并非完美,比如:对渲染出来很小或很细的物体很难拾取,对蛇这种带动画的物体也有同样问题,可以考虑渲染拾取像素时加个矩形等等,这个问题还没有想到完美的解决办法。就目前来说还有个问题就是由于没有进行射线三角形相交测试,所以不能确定鼠标点中点的世界座标,要解决这个问题只要在拾取渲染同时将像素3d座标也输出就可以实现,项目没有这个需求,解决方案就没加这点。

       从这个解决方案也可以看出,其实用像素来进行物体拾取判断的技术本身非常简单,但是要作为一个功能模块集成到项目中或引擎中则还需要做很多工作,这也是ce、ue等通用引擎难得的地方:把很多简单的技术或方法集成到一起相互协调并高效的工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章