【问题标题】:Screen space raycasting屏幕空间光线投射
【发布时间】:2012-12-22 00:02:40
【问题描述】:

我正在使用 OpenGL ES 2.0 和最新的 iOS。

我有一个 3D 模型,我希望用户能够通过点击屏幕来选择模型的不同部分。我找到了this关于将像素空间屏幕坐标转换为世界空间光线的教程,并实施了光线-AABB 相交测试来确定模型的相交部分。

我在模型的看似随机的部分获得了一些点击。所以我需要调试这个功能,但我真的不知道从哪里开始。

我不能准确地画出一条线来代表射线(因为它是从相机出来的,所以它会显示为一个点),所以我可以看到几种调试方法:

  1. 选中模型部分的边界框。那么 OGL ES 有没有一种简单的方法可以在给定最小和最大点的情况下绘制边界框?

  2. 沿光线路径绘制一些 3D 对象。这似乎更复杂。

  3. 实际调试光线投射和交集代码。这似乎是最难完成的,因为算法是众所周知的(我直接从我的 Real-Time Collision Detection 书中进行了交叉测试)。

如果有人可以提供帮助,或者希望我发布一些代码,我真的可以使用它。

这是我转换到世界空间的代码:

- (IBAction)tappedBody:(UITapGestureRecognizer *)sender
{
    if ( !editMode )
    {
        return;
    }
    CGPoint tapPoint = [sender locationOfTouch:0 inView:self.view];
    const float tanFOV = tanf(GLKMathDegreesToRadians(65.0f*0.5f));
    const float width = self.view.frame.size.width,
                height = self.view.frame.size.height,
                aspect = width/height,
                w_2 = width * 0.5,
                h_2 = height * 0.5;

    CGPoint screenPoint;
    screenPoint.x = tanFOV * ( tapPoint.x / w_2 - 1 ) / aspect;
    screenPoint.y = tanFOV * ( 1.0 - tapPoint.y / h_2 );

    GLKVector3 nearPoint = GLKVector3Make(screenPoint.x * NEAR_PLANE, screenPoint.y * NEAR_PLANE, NEAR_PLANE );
    GLKVector3 farPoint = GLKVector3Make(screenPoint.x * FAR_PLANE, screenPoint.y * FAR_PLANE, FAR_PLANE );

    GLKVector3 nearWorldPoint = GLKMatrix4MultiplyVector3( _invViewMatrix, nearPoint );
    GLKVector3 farWorldPoint = GLKMatrix4MultiplyVector3( _invViewMatrix, farPoint );

    GLKVector3 worldRay = GLKVector3Subtract(farWorldPoint, nearWorldPoint);
    NSLog(@"Model matrix: %@", NSStringFromGLKMatrix4(_modelMatrix));
    worldRay = GLKVector4Normalize(worldRay);

    [male intersectWithRay:worldRay fromStartPoint:nearWorldPoint];

    for ( int i =0; i < 3; ++i )
    {
        touchPoint[i] = nearWorldPoint.v[i];
    }
}

这是我获得矩阵的方法:

- (void)update
{
//  _rotation = 0;

    float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, NEAR_PLANE, FAR_PLANE);

    self.effect.transform.projectionMatrix = projectionMatrix;
    GLKMatrix4 baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -5.0f);

    // Compute the model view matrix for the object rendered with ES2
    _viewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 0.0f);
    _modelMatrix = GLKMatrix4Rotate(baseModelViewMatrix, _rotation, 0.0f, 1.0f, 0.0f);
    _modelViewMatrix = GLKMatrix4Rotate(_viewMatrix, _rotation, 0.0f, 1.0f, 0.0f);
    _modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, _modelViewMatrix);

    _invViewMatrix = GLKMatrix4Invert(_viewMatrix, NULL);
    _invMVMatrix = GLKMatrix4Invert(_modelViewMatrix, NULL);

    _normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(_modelViewMatrix), NULL);

    _modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, _modelViewMatrix);

    male.modelTransform = _modelMatrix;

    if ( !editMode )
    {
        _rotation += self.timeSinceLastUpdate * 0.5f;
    }
}

【问题讨论】:

    标签: ios opengl-es-2.0 raycasting


    【解决方案1】:

    我无法准确地画出一条代表射线的线(因为它即将到来 在相机之外它会显示为一个点)

    不要这么快就丢弃它。这不是你想要测试的吗?我的意思是,如果你没有正确地预测事情,那就是这样。但如果你有一个错误,它不会。

    我会先解决这个问题...如果您在手指下看到一个点,则表示转换是正确的,您可以开始研究您指出的其他选项,这些选项更复杂。

    【讨论】:

    • 在我计算的世界空间坐标处绘制它?
    • 是的。如果需要,您还可以绘制屏幕空间点。它也应该与“线点”一起在您的手指下。如果可行,则意味着您的转换代码完全正确.. 几乎“根据定义”。
    • 好吧,世界空间点根本没有画在正确的位置,我在我的问题中添加了一些代码,你能发现我的计算似乎完全错误的任何地方吗?
    • 哦,天啦噜,它的世界空间点正在应用模型的变换,而不仅仅是视图投影变换,让我来解决这个问题,看看它是否有帮助
    • 我找不到问题。您的代码似乎忠于本教程。教程中有一些我不太明白的东西(比如 ASPECT 的划分)。也许你可以尝试另一个教程。我认为您可以使用 GLKMathUnproject 获得两个交点(请参阅*.com/questions/11056391/…)。
    【解决方案2】:

    我不确定为什么这解决了我遇到的问题但正在改变

    screenPoint.x = tanFOV * ( tapPoint.x / w_2 - 1 ) / aspect;
    

    screenPoint.x = tanFOV * ( tapPoint.x / w_2 - 1 ) * aspect;
    

    GLKVector4 nearPoint = GLKVector4Make(screenPoint.x * NEAR_PLANE, screenPoint.y * NEAR_PLANE, NEAR_PLANE, 1 );
    GLKVector4 farPoint = GLKVector4Make(screenPoint.x * FAR_PLANE, screenPoint.y * FAR_PLANE, FAR_PLANE, 1 );
    

    GLKVector4 nearPoint = GLKVector4Make(screenPoint.x * NEAR_PLANE, screenPoint.y * NEAR_PLANE, -NEAR_PLANE, 1 );
    GLKVector4 farPoint = GLKVector4Make(screenPoint.x * FAR_PLANE, screenPoint.y * FAR_PLANE, -FAR_PLANE, 1 );
    

    似乎已经修复了光线投射问题。仍然不确定为什么我的视图矩阵似乎表明相机正在向下看正 Z 轴,但我的对象沿 z 轴被平移为负

    【讨论】: