屏幕空间光线投射

时间:2012-12-19 20:37:20

标签: ios opengl-es-2.0 raycasting

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

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

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

我无法准确地绘制一条代表光线的线(因为它会从相机中出来,它会显示为一个点),所以我可以看到几种调试方法:

  1. 检查模型部分的边界框。那么有一个简单的方法,OGL ES在给定最小和最大点的情况下绘制一个边界框?

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

  3. 实际调试光线投射和交叉码。这似乎是最难实现的,因为算法是众所周知的(我直接参加了我的实时碰撞检测书的交叉测试)。

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

    以下是我转换为世界空间的代码:

    - (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;
        }
    }
    

2 个答案:

答案 0 :(得分:0)

  

我无法准确地画一条代表光线的线(因为它来了   在相机外面它将显示为一个点)

不要这么快丢弃它。这不是你想要测试的吗?我的意思是,如果你没有正确投射,那就是这样。但如果你有一个bug,它就不会。

我将首先使用它...如果您在手指下方看到一个点,转换是正确的,您可以开始调查您指出的其他选项,这些选项更复杂。

答案 1 :(得分:0)

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

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轴被转换为负