ibr*_*mir 1 iphone 3d opengl-es ray-picking
我找不到使用光线追踪方法在3D中拾取的正确和可理解的表达方式.有没有人用任何语言实现这个算法?直接共享工作代码,因为由于伪代码无法编译,所以它们通常用缺少的部分编写.
你所拥有的是屏幕上2D的位置.首先要做的是将该点从像素转换为标准化设备坐标 - -1到1.然后,您需要在该点表示的3D空间中找到该线.为此,您需要3D应用程序用于创建投影和相机的变换矩阵/ ces.
通常,您有3个matrics:投影,视图和模型.为对象指定顶点时,它们位于"对象空间"中.乘以模型矩阵给出了"世界空间"中的顶点.再次乘以视图矩阵给出"眼睛/相机空间".再次乘以投影给出"剪辑空间".剪辑空间具有非线性深度.将Z组件添加到鼠标坐标会将它们放入剪辑空间.您可以在任何线性空间中执行线/对象相交测试,因此您必须至少将鼠标坐标移动到眼睛空间,但在世界空间(或对象空间,取决于您的场景图)中执行相交测试更方便.
要将鼠标坐标从剪辑空间移动到世界空间,请添加Z分量并乘以逆投影矩阵,然后乘以逆摄像机/视图矩阵.要创建一条线,将计算沿Z的两个点 - from和to.

在下面的示例中,我有一个对象列表,每个对象都有一个位置和边界半径.当然,交叉点永远不会完美匹配,但现在它的效果还不错.这不是伪代码,但它使用我自己的矢量/矩阵库.你必须用自己的地方替代.
vec2f mouse = (vec2f(mousePosition) / vec2f(windowSize)) * 2.0f - 1.0f;
mouse.y = -mouse.y; //origin is top-left and +y mouse is down
mat44 toWorld = (camera.projection * camera.transform).inverse();
//equivalent to camera.transform.inverse() * camera.projection.inverse() but faster
vec4f from = toWorld * vec4f(mouse, -1.0f, 1.0f);
vec4f to = toWorld * vec4f(mouse, 1.0f, 1.0f);
from /= from.w; //perspective divide ("normalize" homogeneous coordinates)
to /= to.w;
int clickedObject = -1;
float minDist = 99999.0f;
for (size_t i = 0; i < objects.size(); ++i)
{
float t1, t2;
vec3f direction = to.xyz() - from.xyz();
if (intersectSphere(from.xyz(), direction, objects[i].position, objects[i].radius, t1, t2))
{
//object i has been clicked. probably best to find the minimum t1 (front-most object)
if (t1 < minDist)
{
minDist = t1;
clickedObject = (int)i;
}
}
}
//clicked object is objects[clickedObject]
Run Code Online (Sandbox Code Playgroud)
相反的intersectSphere,你可以使用边界框或其他隐式几何体,或者交叉网状的三角形(这可能需要建立一个kd树的性能原因).
[编辑]
这是线/球体相交的实现(基于上面的链接).它假定球在原点,所以强似from.xyz()作为p,给from.xyz() - objects[i].position.
//ray at position p with direction d intersects sphere at (0,0,0) with radius r. returns intersection times along ray t1 and t2
bool intersectSphere(const vec3f& p, const vec3f& d, float r, float& t1, float& t2)
{
//http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
float A = d.dot(d);
float B = 2.0f * d.dot(p);
float C = p.dot(p) - r * r;
float dis = B * B - 4.0f * A * C;
if (dis < 0.0f)
return false;
float S = sqrt(dis);
t1 = (-B - S) / (2.0f * A);
t2 = (-B + S) / (2.0f * A);
return true;
}
Run Code Online (Sandbox Code Playgroud)