iPhone4 OpenGL ES GLuProject返回错误的y坐标

时间:2011-08-26 08:54:49

标签: iphone opengl-es 3d glu

我对iPhone 4 OpenGL ES应用程序有一个令人困惑的问题,我一直试图打开和关闭几个月,尽管在这个网站上有一些非常有用和诱人的提示和建议,但它已经走到了尽头。

我正在编写一个3D游戏,它简单地绘制块并允许用户将它们移动到各种排列中,并且应用程序的大部分都是用C ++编写的。

我的问题是我正在尝试使用我在这里找到源代码的GLuUnproject:

http://webcvs.freedesktop.org/mesa/Mesa/src/glu/mesa/project.c?view=markup

解释用户选择的3d点(以及块),以便移动和旋转它,我将其设置为浮点而不是双精度。

请注意,我已将此来源与网络上的其他版本进行了比较,看起来是一致的。

我使用以下代码来获取光线矢量:

Ray RenderingEngine::GetRayVector( vec2 winPos ) const
{
    // Get the last matrices used
    glGetFloatv( GL_MODELVIEW_MATRIX, __modelview ); 
    glGetFloatv( GL_PROJECTION_MATRIX, __projection );
    glGetIntegerv( GL_VIEWPORT, __viewport );

    // Flip the y coordinate
    winPos.y = (float)__viewport[3] - winPos.y;

    // Create vectors to be set
    vec3 nearPoint;
    vec3 farPoint;
    Ray rayVector;

    //Retrieving position projected on near plan
    gluUnProject( winPos.x, winPos.y , 0, 
                 __modelview, __projection, __viewport, 
                  &nearPoint.x, &nearPoint.y, &nearPoint.z);

    //Retrieving position projected on far plan
    gluUnProject( winPos.x, winPos.y,  1, 
                 __modelview, __projection, __viewport, 
                  &farPoint.x, &farPoint.y, &farPoint.z);

    rayVector.nearPoint = nearPoint;
    rayVector.farPoint = farPoint;

    //Return the ray vector
    return rayVector;
}

用于描绘从近平面到远平面的返回光线的矢量代码是直截了当的,我发现屏幕底部附近的块被正确识别,但是当屏幕向上移动时,似乎存在越来越大的差异在报告的y值和所选点的预期y值。

我还尝试使用GLuProject手动检查为我的世界坐标生成了哪些屏幕坐标,如下所示:

vec3 RenderingEngine::GetScreenCoordinates( vec3 objectPos ) const
{

    // Get the last matrices used
    glGetFloatv( GL_MODELVIEW_MATRIX, __modelview ); 
    glGetFloatv( GL_PROJECTION_MATRIX, __projection );
    glGetIntegerv( GL_VIEWPORT, __viewport );

    vec3 winPos;

    gluProject(objectPos.x, objectPos.y, objectPos.z , 
                   __modelview, __projection, __viewport, 
                   &winPos.x, &winPos.y, &winPos.z);

    // Swap the y value
    winPos.y = (float)__viewport[3] - winPos.y;

    return winPos;
}  

同样,结果与光线跟踪方法一致,因为当用户点击屏幕上方时,GLuProjected y坐标变得越来越错误。

例如,当touchesBegan事件直接报告的点击位置为(246,190)时,计算的位置为(246,215),y差异为25。

当touchesBegan事件直接报告的点击位置为(246,398)时,计算出的位置为(246,405),y差异为7.

x坐标似乎是斑点。

我注意到当视口高度设置为480(全屏高度)时,layer.bounds.size.height报告为436。图层边界宽度报告为320,也是视口的宽度。

无论我使用什么视口大小或是否在窗口顶部显示状态屏幕,436的值似乎都是固定的。

在下面的调用之前尝试将bounds.size.height设置为480:

[my_context
        renderbufferStorage:GL_RENDERBUFFER
        fromDrawable: eaglLayer];

但这似乎被忽略了,后来报道的高度为436:

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                GL_RENDERBUFFER_HEIGHT_OES, &height);

我已经看到一些关于点和像素的差异以及可能需要缩放的讨论,但是很难有效地使用这些信息,因为这些暗示了差异是由于iPhone 4的视网膜显示分辨率以及不同的缩放比例将是模拟器和实际设备所必需的。但是,据我所知,模拟器和设备的行为一致。

2011年8月30日由于没有得到任何反馈 - 我能提供更多信息以使问题更容易处理吗?

2011年8月31日OpenGL设置和显示代码如下:

- (id) initWithCoder:(NSCoder*)coder
{    
    if ((self = [super initWithCoder:coder]))
    {

        // Create OpenGL friendly layer to draw in
        CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
        eaglLayer.opaque = YES;

        // eaglLayer.bounds.size.width and eaglLayer.bounds.size.height are 
        // always 320 and 436 at this point

        EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES1;
        m_context = [[EAGLContext alloc] initWithAPI:api];

        // check have a context
        if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
            [self release];
            return nil;
        }

        glGenRenderbuffersOES(1, &m_colorRenderbuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);

        [m_context
            renderbufferStorage:GL_RENDERBUFFER
            fromDrawable: eaglLayer];

        UIScreen *scr = [UIScreen mainScreen];
        CGRect rect = scr.applicationFrame;
        int width = CGRectGetWidth(rect);    // Always 320
        int height = CGRectGetHeight(rect);  // Always 480 (status bar not displayed)

        // Initialise the main code
        m_applicationEngine->Initialise(width, height);

            // This is the key c++ code invoked in Initialise call shown here indented

            // Setup viewport
            LowerLeft = ivec2(0,0);
            ViewportSize = ivec2(width,height);

            // Code to create vertex and index buffers not shown here
            // …

            // Extract width and height from the color buffer.
            int width, height;
            glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                            GL_RENDERBUFFER_WIDTH_OES, &width);
            glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                            GL_RENDERBUFFER_HEIGHT_OES, &height);

            // Create a depth buffer that has the same size as the color buffer.
            glGenRenderbuffersOES(1, &m_depthRenderbuffer);
            glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer);
            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
                                     width, height);

            // Create the framebuffer object.
            GLuint framebuffer;
            glGenFramebuffersOES(1, &framebuffer);
            glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);
            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
                                         GL_RENDERBUFFER_OES, m_colorRenderbuffer);
            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
                                     GL_RENDERBUFFER_OES, m_depthRenderbuffer);
            glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);


            // Set up various GL states.
            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_NORMAL_ARRAY);
            glEnable(GL_LIGHTING);
            glEnable(GL_LIGHT0);
            glEnable(GL_DEPTH_TEST);

        // ...Back in initiWithCoder

        // Do those things which need to happen when the main code is reset
        m_applicationEngine->Reset();

            // This is the key c++ code invoked in Reset call shown here indented

            // Set initial camera position where
            // eye=(0.7,8,-8), m_target=(0,4,0), CAMERA_UP=(0,-1,0)
            m_main_camera = mat4::LookAt(eye, m_target, CAMERA_UP); 


        // ...Back in initiWithCoder
        [self drawView: nil];
        m_timestamp = CACurrentMediaTime();

        // Create timer object that allows application to synchronise its 
        // drawing to the refresh rate of the display.
        CADisplayLink* displayLink;
        displayLink = [CADisplayLink displayLinkWithTarget:self
                                 selector:@selector(drawView:)];

        [displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                 forMode:NSDefaultRunLoopMode];
    }
    return self;
}



- (void) drawView: (CADisplayLink*) displayLink
{

    if (displayLink != nil) {

        // Invoke main rendering code
        m_applicationEngine->Render();

            // This is the key c++ code invoked in Render call shown here indented

            // Do the background
            glClearColor(1.0f, 1.0f, 1.0f, 1);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            // A set of objects are provided to this method
            // for each one (called visual below) do the following:

            // Set the viewport transform.
            glViewport(LowerLeft.x, LowerLeft.y, ViewportSize.x, ViewportSize.y);

            // Set the model view and projection transforms
            // Frustum(T left, T right, T bottom, T top, T near, T far)
            float h = 4.0f * size.y / size.x;
            mat4 modelview = visual->Rotation * visual->Translation * m_main_camera;
            mat4 projection = mat4::Frustum(-1.5, 1.5, h/2, -h/2, 4, 14);

            // Load the model view matrix and initialise
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glLoadMatrixf(modelview.Pointer());

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glLoadMatrixf(projection.Pointer());

            // Draw the surface - code not shown
            // …    

        // ...Back in drawView
        [m_context presentRenderbuffer:GL_RENDERBUFFER];
    }
}

2 个答案:

答案 0 :(得分:0)

当调整持有渲染器的视图的大小时,将以这种方式通知它:

- (void) layoutSubviews
{
  [renderer resizeFromLayer:(CAEAGLLayer*)self.layer];
  [self drawView:nil];
}

- (BOOL) resizeFromLayer:(CAEAGLLayer *)layer
{   
    // Allocate color buffer backing based on the current layer size
  glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderBuffer);
  [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer];
  glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
  glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

  if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
  {
    NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
    return NO;
  }

  [self recreatePerspectiveProjectionMatrix];
  return YES;
}

请注意,由于视图端口大小已更改,因此应正确重新创建透视矩阵。它会对非项目结果产生影响。

与比例问题相关:

内部视图初始化获取比例因子:

    CGFloat scale = 1;
    if ([self respondsToSelector:@selector(getContentScaleFactor:)])
    {
        self.contentScaleFactor = [[UIScreen mainScreen] scale];
        scale = self.contentScaleFactor;
    }

在标准和视网膜显示器上,视图尺寸实际上几乎相同,320px宽,但视网膜的渲染层尺寸将增加一倍,640px。在opengl渲染器空间和视图空间之间进行转换时,应考虑比例因子。

<强>加了: 尝试更改获取和设置初始化代码中的宽度和高度参数的顺序:

而不是:

    int width = CGRectGetWidth(rect);    // Always 320
    int height = CGRectGetHeight(rect);  // Always 480 (status bar not displayed)

    // Initialise the main code
    m_applicationEngine->Initialise(width, height);

        // This is the key c++ code invoked in Initialise call shown here indented

        // Setup viewport
        LowerLeft = ivec2(0,0);
        ViewportSize = ivec2(width,height);

        // Code to create vertex and index buffers not shown here
        // …

        // Extract width and height from the color buffer.
        int width, height;
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                        GL_RENDERBUFFER_WIDTH_OES, &width);
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                        GL_RENDERBUFFER_HEIGHT_OES, &height);

尝试此顺序(不要使用视图中的大小):

        // Code to create vertex and index buffers not shown here
        // …

        // Extract width and height from the color buffer.
        int width, height;
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                        GL_RENDERBUFFER_WIDTH_OES, &width);
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                        GL_RENDERBUFFER_HEIGHT_OES, &height);

    // Initialise the main code
    m_applicationEngine->Initialise(width, height);

        // This is the key c++ code invoked in Initialise call shown here indented

        // Setup viewport
        LowerLeft = ivec2(0,0);
        ViewportSize = ivec2(width,height);

还要确保为全屏布局设置了UIController参数:

self.wantsFullScreenLayout = YES;

之后,对于iphone 4,宽度和高度应该是640x960,而contentScaleFactor应该是2。

但是,请注意layoutSubviews是标准的UIView函数,它是我获取屏幕大小和调整投影或平截头体矩阵的唯一地方。

答案 1 :(得分:0)

好吧......现在感觉有些愚蠢......

问题在于,我使用的视图实际上是436像素高,这是我在试验允许在主窗口上为常用导航栏留出空间时已经设置的,我不再使用。

将此设置回480可以解决问题。

向那些看过这个的人致歉,尤其是那些回应的人。

在经过几个月的挫折之后,我现在要走出困境了!