射线相交未击中目标 [英] ray intersection misses the target

查看:79
本文介绍了射线相交未击中目标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试选择3d点.我读了很多网站,但是我的代码不起作用.

I'm trying to pick a 3d point. I read various sites but my code doesn't work.

鼠标右键:

glGetFloatv(GL_MODELVIEW_MATRIX,mv_mat)
glGetFloatv(GL_PROJECTION_MATRIX,p_mat)

ip_mat = np.linalg.inv(mat4(p_mat))

# clip = array[
# (2*x)/window_width-1
# 1-(y*2)/window.height
# -1
# 1
camera_coord = np.dot(ip_mat,clip)
camera_coord = np.array([camera_coord[0,0],camera_coord[0,1],-1,0])

imv_mat = np.linalg.inv(mat4(mv_mat))

ray_world = np.dot(imv_mat,camera_coord)
ray_world = np.array([ray_world[0],ray_world[1],ray_world[2]])
ray_world = ray_world/np.linalg.norm(ray_world)

Intersect_sphere函数:

v = np.array([model.rx,model.ry,model.rz]) - np.array([-0.5, 0.5, 0])
b = 2 * np.dot(v, ray_world)
c = np.dot(v, v) - 1 * 1
delta = b * b - 4 * c

if (delta > 0):
    print('select')
    return True

return False

编辑:我发现了一个错字.即使更改了代码,仍然无法正常工作.

edit: I found a typo. Even after changing the code still does not work.

推荐答案

如果要在窗口中选择一个点,则必须将窗体窗口坐标转换为世界坐标或对象坐标.

If you want to pick a point in the window, then you have to transform form window coordinates to world coordinates or object coordinates.

要将窗口坐标映射到对象坐标,请 <可以使用c0> .
gluUnProject 的参数是键入GLdouble.

To map window coordinates to object coordinates, gluUnProject can be used.
The parameters to gluUnProject are of type GLdouble.

为类型为GLdouble的投影矩阵和视图矩阵创建一个数组,为视口矩形创建一个类型为GLint的数组:

Create an array for the projection matrix and the view matrix of type GLdouble and an array of type GLint for the viewport rectangle:

self.mv_mat = (GLdouble * 16)()
self.p_mat  = (GLdouble * 16)()
self.v_rect = (GLint * 4)()

获取当前的投影矩阵,模型视图矩阵和视口矩形:

Get the current projection matrix, model view matrix and viewport rectangle:

glGetDoublev(GL_MODELVIEW_MATRIX, self.mv_mat)
glGetDoublev(GL_PROJECTION_MATRIX, self.p_mat)
glGetIntegerv(GL_VIEWPORT, self.v_rect)

在视口上绘制3D场景的2维(透视)投影.从一个位置(即摄像机位置)观察场景.为了找到在窗口中被拾取"的物体,您必须找到物体所在位置的观察光线.一条射线由2个点定义.在拾取"窗口位置上找到摄像机附近的点和场景深度中的远点,以定义光线.拾取的对象是最接近相机的那个对象.从相机位置看,在归一化设备空间中,具有相同x和y坐标的所有点都在同一条射线上.
窗口空间中点的第一和第二坐标是像素的x和y坐标,第三坐标是[0,1]范围内的深度.
因此,从相机附近到远处的光线槽坐标(x,y)由两个点 p0 p1 定义,其中:

On the viewport is drawn the 2 dimensional (perspective) projection of a 3 dimensional scene. The scene is looked at from one point, the camera position. To find the object which is "picked" in the window, you have to find a the viewing ray where the object is on. A ray is defined by 2 points. Find a point near to the camera and a point far in the depth of the scene, which are on the "picked" window position to define the ray. The picked object is that object, which is closest to the camera. In normalized device space, all points which have the same x and y coordinate are on the same ray, as seen from the camera position.
The 1st and 2nd coordinates of a point in window space are the x and y coordinate in pixel, the 3rd coordinate is the depth in range [0, 1].
So a ray trough coordinate (x,y) from near the camera to the far depth is defined by the 2 points p0 and p1, where:

p0 = (x, y, 0)
p1 = (x, y, 1)

此点必须通过gluUnProject转换为2个世界空间:

This to points have to be transformed 2 world space by gluUnProject:

ray_near = [GLdouble() for _ in range(3)]
ray_far  = [GLdouble() for _ in range(3)]
gluUnProject(x, y, 0, mv_mat, p_mat, v_rect, *ray_near)
gluUnProject(x, y, 1, mv_mat, p_mat, v_rect, *ray_far)

如果从球的中心点到射线的最近点的距离小于或等于球的半径,则射线与球相交.

A ray intersects a sphere, if distance from the center point of the sphere, to the nearest point on the ray, is less or equal the radius of the sphere.

计算射线的归一化方向:

Calculate the normalized direction of the ray:

p0 = [v.value for v in ray_near]
p1 = [v.value for v in ray_far]

r_dir = np.subtract(p0, p1)
r_dir = r_dir / np.linalg.norm(r_dir)

计算射线上最接近球体中心点的点:

Calculate the nearest point on the ray to the center point of the sphere:

p0_cpt = np.subtract(p0, cpt)
near_pt = np.subtract(p0, r_dir * np.dot(p0_cpt, r_dir))

计算射线上的点到中心点的距离:

Calculate the distance of the point on the ray to the center point:

dist = np.linalg.norm(np.subtract(near_pt, cpt))

如果距离小于或等于球体的半径,则射线会撞击球体:

If the distance is less or equal the radius of the sphere, then the ray hits the sphere:

isIntersecting = dist <= radius

请参见简短的 PyGlet 示例:

from pyglet.gl import *
from pyglet.window import key
import numpy as np

class Window(pyglet.window.Window):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sphere = gluNewQuadric() 
        self.vp_valid = False
        self.mouse_pos = (0, 0)
        self.mv_mat = (GLdouble * 16)()
        self.p_mat  = (GLdouble * 16)()
        self.v_rect = (GLint * 4)() 

    def on_resize(self, width, height):
        self.vp_valid = False

    def isectSphere(self, p0, p1, cpt, radius):

        # normalized ray direction
        r_dir = np.subtract(p0, p1)
        r_dir = r_dir / np.linalg.norm(r_dir)

        # nearest point on the ray to the sphere
        p0_cpt = np.subtract(p0, cpt)
        near_pt = np.subtract(p0, r_dir * np.dot(p0_cpt, r_dir))

        # distance to center point
        dist = np.linalg.norm(np.subtract(near_pt, cpt))

        # intersect if dist less or equal the radius of the sphere 
        return dist <= radius

    def on_draw(self):

        if not self.vp_valid:
            self.vp_valid = True
            glViewport(0, 0, self.width, self.height)
            glMatrixMode(GL_PROJECTION)
            glLoadIdentity()
            gluPerspective(45, self.width/self.height, 0.1, 50.0)
            glMatrixMode(GL_MODELVIEW)
            glLoadIdentity()
            gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)

            glGetDoublev(GL_MODELVIEW_MATRIX, self.mv_mat)
            glGetDoublev(GL_PROJECTION_MATRIX, self.p_mat)
            glGetIntegerv(GL_VIEWPORT, self.v_rect)

        temp_val = [GLdouble() for _ in range(3)]
        gluUnProject(*self.mouse_pos, 0, self.mv_mat, self.p_mat, self.v_rect, *temp_val)
        self.mouse_near = [v.value for v in temp_val]
        gluUnProject(*self.mouse_pos, 1, self.mv_mat, self.p_mat, self.v_rect, *temp_val)
        self.mouse_far = [v.value for v in temp_val]

        isect_a = self.isectSphere(self.mouse_near, self.mouse_far, [-1.5, 0, 0], 1)
        isect_b = self.isectSphere(self.mouse_near, self.mouse_far, [1.5, 0, 0], 1)

        glEnable(GL_DEPTH_TEST)
        glEnable(GL_LIGHTING)
        glShadeModel(GL_SMOOTH)
        glEnable(GL_COLOR_MATERIAL)
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)

        glEnable(GL_LIGHT0)
        glLightfv(GL_LIGHT0, GL_AMBIENT, (GLfloat *4)(0.1, 0.1, 0.1, 1))
        glLightfv(GL_LIGHT0, GL_DIFFUSE, (GLfloat *4)(1.0, 1.0, 1.0, 1))
        glLightfv(GL_LIGHT0, GL_POSITION, (GLfloat *4)(0, -1, 0, 0))

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        glPushMatrix()

        glTranslatef(-1.5, 0, 0)
        if isect_a:
            glColor4f(1.0, 0.5, 0.5, 1)
        else:
            glColor4f(0.5, 0.2, 0.2, 1)
        gluSphere(self.sphere, 1.0, 32, 16)

        glTranslatef(3, 0, 0)
        if isect_b:
            glColor4f(0.5, 0.5, 1.0, 1)
        else:
            glColor4f(0.2, 0.2, 0.5, 1)
        gluSphere(self.sphere, 1.0, 32, 16)

        glPopMatrix()

    def on_mouse_motion(self,x,y,dx,dy):
        self.mouse_pos = (x, y)

if __name__ == "__main__":
    window = Window(width=800, height=600, resizable=True)
    pyglet.app.run()

这篇关于射线相交未击中目标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆