iPhone4 OpenGL ES GLuProject возвращает неправильную координату Y - PullRequest
0 голосов
/ 26 августа 2011

У меня непонятная проблема с приложением 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 точечная.

Я заметил, что layer.bounds.size.height сообщается как 436, когда высота области просмотра установлена ​​на 480 (высота полного экрана). Ширина границ слоя указывается как 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 и с другим масштабированием потребуется для симулятора и фактического устройства. Однако, насколько я могу судить, симулятор и устройство ведут себя согласованно.

30-Aug-2011 Поскольку я не получаю отзывов об этом - могу ли я предоставить больше информации, чтобы сделать вопрос более податливым?

31 августа 2011 г. Настройка 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 голосов
/ 02 сентября 2011

Ну ... я чувствую себя немного глупо ...

Проблема заключалась в том, что используемый мной вид имел высоту 436 пикселей, что я установил несколько лет назад, когда экспериментировал с выделением места для общей панели навигации в главном окне, которое я больше не использую.

Установка этого значения на 480 решила проблему.

Извинения тем, кто на это посмотрел, и особенно тем, кто откликнулся.

Теперь я собираюсь пойти и избавить себя от страданий после нескольких месяцев разочарования!

0 голосов
/ 30 августа 2011

При изменении размера представления, содержащего средство визуализации, оно будет уведомлено следующим образом:

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

Размер изображения практически одинаков на стандартном дисплее и сетчатке, шириной 320 пикселей, но размер слоя рендеринга для сетчатки увеличится вдвое, 640 пикселей.При преобразовании между пространством визуализации opengl и пространством просмотра следует учитывать коэффициент масштабирования.

Добавлено: Попробуйте изменить порядок получения и установки параметров width и height внутри кода инициализации:

Вместо этого:

    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, и это единственное место, где я получаю размер экрана и настраиваю проекцию или матрицу усеченного конуса.

...