iPhon上的屏幕到世界问题 e
我有一个在EAGLView中渲染的3D模型(CUBE),我希望能够检测到何时触摸立方体的给定面(从任何方向角度)的中心。听起来很简单但不是......
问题:
如何准确地将屏幕坐标(触摸点)与世界坐标(OpenGL 3D空间中的位置)相关联?当然,将给定点转换为屏幕/世界轴的“百分比”似乎是合乎逻辑的解决方案,但是当我需要缩放或旋转3D空间时会出现问题。注意:旋转&放大和缩小3D空间将改变2D屏幕坐标与3D世界坐标的关系......此外,您必须允许3D空间中的视点和对象之间的“距离”。起初,这似乎是一个“简单的任务”,但是当您实际检查需求时会发生变化。而且我没有找到人们在iPhone上这样做的例子。这通常是怎么做的?
'简单'的任务?:
当然,人们可能会承担编写API的任务,作为屏幕和世界之间的中间人,但创建这样一个框架的任务需要一些严肃的设计,并且可能需要“时间”去做 - 不是那样的可以在4小时内单人 ...... 4个小时恰好是我的截止日期。
问题:
答案 0 :(得分:7)
您现在可以在http://code.google.com/p/iphone-glu/中找到 gluUnProject 。我与iphone-glu项目没有联系,还没有尝试过,我只是想分享这个链接。
你会如何使用这样的功能? This PDF提到:
实用程序库例程 gluUnProject()执行转换的这种反转。给定一个位置的三维窗口坐标以及影响它们的所有变换,gluUnProject()返回它起源的世界坐标。
int gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz,
const GLdouble modelMatrix[16], const GLdouble projMatrix[16],
const GLint viewport[4], GLdouble *objx, GLdouble *objy, GLdouble *objz);
使用由模型视图矩阵(modelMatrix),投影矩阵(projMatrix)和视口(视口)定义的变换将指定的窗口坐标(winx,winy,winz)映射到对象坐标。生成的对象坐标以objx,objy和objz返回。函数返回 GL_TRUE ,表示成功,或 GL_FALSE ,表示失败(例如不可逆矩阵)。此操作不会尝试将坐标剪切到视口或消除超出 glDepthRange()的深度值。
试图扭转转型过程存在固有的困难。二维屏幕位置可以源自三维空间中整条线上的任何位置。为了消除结果的歧义,gluUnProject()要求提供窗口深度坐标(winz),并根据glDepthRange()指定winz。对于glDepthRange()的默认值,winz at 0.0将在近剪裁平面处请求变换点的世界坐标,而winz为1.0将请求远剪裁平面处的点。
示例3-8(再次参见the PDF)通过读取鼠标位置并确定从其转换的近和远剪裁平面的三维点来演示gluUnProject()。计算出的世界坐标打印到标准输出,但渲染窗口本身只是黑色。
在性能方面,I found this quickly via Google作为使用gluUnProject可能不想做的事情的一个例子,带有what might lead to a better alternative的链接。我完全不知道它对iPhone有多适用,因为我仍然是OpenGL ES的新手。一个月后再问我一次。 ; - )
答案 1 :(得分:3)
您需要拥有opengl投影和模型视图矩阵。将它们相乘以获得模型视图投影矩阵。反转此矩阵以获得将剪辑空间坐标转换为世界坐标的矩阵。变换您的触摸点,使其对应于剪辑坐标:屏幕中心应为零,而X和Y的边缘应分别为+ 1 / -1。
构造两个点,一个在(0,0,0),一个在(touch_x,touch_y,-1),并通过逆模型视图投影矩阵进行变换。
做透视鸿沟的倒数。
你应该得到两点描述从摄像机中心到“远距离”(远星)的一条线。
根据模型的简化边界框进行拾取。你应该能够在网上找到很多光线/盒子交叉算法。
另一个解决方案是将每个模型以略微不同的颜色绘制到屏幕外缓冲区中,并从那里读取触摸点处的颜色,告诉您触摸了哪个brich。
这是我使用子弹物理为小项目编写的游标的来源:
float x=((float)mpos.x/screensize.x)*2.0f -1.0f;
float y=((float)mpos.y/screensize.y)*-2.0f +1.0f;
p2=renderer->camera.unProject(vec4(x,y,1.0f,1));
p2/=p2.w;
vec4 pos=activecam.GetView().col_t;
p1=pos+(((vec3)p2 - (vec3)pos) / 2048.0f * 0.1f);
p1.w=1.0f;
btCollisionWorld::ClosestRayResultCallback rayCallback(btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z));
game.dynamicsWorld->rayTest(btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z), rayCallback);
if (rayCallback.hasHit())
{
btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject);
if(body==game.worldBody)
{
renderer->setHighlight(0);
}
else if (body)
{
Entity* ent=(Entity*)body->getUserPointer();
if(ent)
{
renderer->setHighlight(dynamic_cast<ModelEntity*>(ent));
//cerr<<"hit ";
//cerr<<ent->getName()<<endl;
}
}
}
答案 2 :(得分:2)
想象一条从观察者眼睛延伸的线 通过屏幕触摸点进入您的3D模型空间。
如果该线与任何立方体的面相交,则用户触摸了立方体。
答案 3 :(得分:1)
有两种解决方案可供选择。它们都应该实现最终目标,尽管是通过不同的方式:而不是回答“鼠标下的世界坐标是什么?”,他们回答“鼠标下呈现什么对象?”的问题。
一种是将模型的简化版本绘制到离屏缓冲区,使用不同的颜色渲染每个面的中心(并调整光照以保持颜色相同)。然后,您可以在缓冲区中检测这些颜色(例如像素图),并将鼠标位置映射到它们。
另一种是使用OpenGL选择。有一个体面的教程here。基本思路是将OpenGL置于选择模式,将视口限制在感兴趣点周围的一个小(可能是3x3或5x5)窗口,然后使用OpenGL“名称”渲染场景(或其简化版本)(整数)标识符)以识别构成每个面的组件。在此过程结束时,OpenGL可以为您提供在选择视口中呈现的名称列表。将这些标识符映射回原始对象将允许您确定鼠标光标下的对象。
答案 4 :(得分:0)
Google for opengl screen to world(例如,有一个帖子,有人想在GameDev.net上完全按照你要求的那样做)。有一个gluUnProject函数正是这样做的,但它在iPhone上不可用,因此您必须移植它(请参阅Mesa项目中的this source)。或者某个地方已经有一些公开的来源?