我找不到用光线追踪方法在3D中正确且易懂的拾取表达方式。是否有人已经在任何编程语言中实现了这个算法?请分享可直接工作的代码,因为伪代码无法编译,通常会缺少一些部分。
我找不到用光线追踪方法在3D中正确且易懂的拾取表达方式。是否有人已经在任何编程语言中实现了这个算法?请分享可直接工作的代码,因为伪代码无法编译,通常会缺少一些部分。
您拥有的是屏幕上的2D位置。首先需要将该点从像素转换为标准化设备坐标-1到1。然后您需要找到点所代表的3D空间中的线。为此,需要使用您的3D应用程序用于创建投影和相机的变换矩阵/ces。
通常您拥有3个矩阵:投影、视图和模型。当您为对象指定顶点时,它们处于“对象空间”中。通过模型矩阵乘以顶点可在“世界空间”中获得顶点。再次乘以视图矩阵会给出“眼睛/相机空间”。再次乘以投影矩阵可得到“剪辑空间”。剪辑空间具有非线性深度。向鼠标坐标添加Z分量可将其放置在剪辑空间内。可以在任何线性空间中执行线条/对象交集测试,因此您必须至少将鼠标坐标移动到眼睛空间,但更方便的是在世界空间(或对象空间,这取决于您的场景图)中执行交集测试。
将鼠标坐标从剪辑空间移动到世界空间,添加Z分量并乘以逆投影矩阵,然后是逆相机/视图矩阵。为了创建一条线,将计算沿Z的两个点-从和到。
在以下示例中,我有一个对象列表,每个对象都具有位置和边界半径。交集当然永远不完全匹配,但现在还可以使用它。这不是伪代码,但它使用了我的自己的向量/矩阵库。您必须在某些地方进行替换。
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]
可以使用包围盒或其他隐式几何图形,或相交网格的三角形(这可能需要为性能原因构建kd树),而不是使用intersectSphere
。
[编辑]
这里是基于上面链接的一条线段/球体相交实现。它假设球体在原点,所以将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;
}
vec4f from = toWorld * vec4f(mouse, -1.0f, 1.0f);
vec4f to = toWorld * vec4f(mouse, 1.0f, 1.0f);
intersectSphere()
函数的实现? - undefinedto
向量的z
参数太高,而且w
参数总是接近于1。所以在这种情况下,除以w
不是一个解决办法。我的更新后的代码 这里是日志 - undefined