Перспектива с фиксированным соотношением сторон (1:1) в OpenGL и Qt5 (QGLWidget)

Я пытаюсь преобразовать куб в QGLWidgetput внутри QSplitter. Я бы хотел, чтобы у него было соотношение 1: 1, чтобы оно было кубом и сохраняло это соотношение независимо от размеров окна. Однако я не смог заставить это работать. Что бы я ни делал, куб тянется вместе с окном.

У меня есть две дополнительные проблемы с тем, как работает код.

  • matrix.setToIdentity() сбрасывает все, кроме проекции и перевода, которые происходят в конце initializeGL(), поэтому, когда я вызываю ее в resizeGL(), проекция объединяется с предыдущей, даже если я перед этим установил матрицу в идентичность.

  • Аргумент aspect ratio из matrix.perspective(), кажется, ничего не делает. Я пробовал несколько значений безрезультатно.

  • Почему-то проекция орфографическая, а не перспективная.

  • Матрица по-прежнему является тождеством после perspective(), но только если я вызываю setToIdentity() и perspective() в resizeGL в этом порядке.

  • Матричные операции в initializeGL() и resizeGL() обрабатываются по-разному. Если я не вызову translate() и perspective() в initializeGL(), куб не появится, даже если я сделаю это позже в resizeGL().

Кто-нибудь может мне это объяснить? Я думал, что класс QMatrix4x4 предназначен для хранения матриц для 3D-преобразований, поэтому мне не нужно реализовывать их самостоятельно.

Я просмотрел некоторые учебные пособия, но либо они используют какие-то матрицы аналогичным образом, либо они, похоже, используют устаревшие функции OpenGL. Пробовал приводить ширину и высоту к числам с плавающей запятой и использовать отдельные матрицы для преобразований.

Как заставить работать соотношение 1:1?

Соответствующий фрагмент моего кода:

void ModelView::initializeGL()
{
    m_program = new QGLShaderProgram(this);
    m_program->addShaderFromSourceCode(QGLShader::Vertex, vertexShaderSource);
    m_program->addShaderFromSourceCode(QGLShader::Fragment, fragmentShaderSource);
    m_program->link();

    GLuint shader_id = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(shader_id, 1, vertexShaderSource, NULL);
    glCompileShader(shader_id);
    ShaderIds[2] = shader_id;

    shader_id = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(shader_id, 1, fragmentShaderSource, NULL);
    glCompileShader(shader_id);
    ShaderIds[1] = shader_id;

    glAttachShader(ShaderIds[0], ShaderIds[1]);
    glAttachShader(ShaderIds[0], ShaderIds[2]);

    glLinkProgram(ShaderIds[0]);*/

    matrix.setToIdentity(); //--------------------------------------

    float aspect = (((float)width())/((float)height()));

    matrix.perspective(60, aspect, 0.1, 100.0);
    matrix.translate(0, 0, -2);
}

void ModelView::paintGL()
{
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glClearColor(.5f, .5f, .5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glViewport(0, 0, width(), height());

    m_program->bind();

    m_posAttr = m_program->attributeLocation("posAttr");
    m_colAttr = m_program->attributeLocation("colAttr");
    m_matrixUniform = m_program->uniformLocation("matrix");
    m_program->setUniformValue(m_matrixUniform, matrix); //-----------------

    glGenBuffers(1, &BufferId);
    glGenBuffers(1, &IndexBufferId);

    glBindBuffer(GL_ARRAY_BUFFER, BufferId);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId);

    glBufferData(GL_ARRAY_BUFFER, BufferSize, Vertices, GL_STATIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);

    glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, VertexSize, 0);
    glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, VertexSize, (GLvoid *)RgbOffset);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, NULL);

    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);

    m_program->release();

}

void ModelView::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
    matrix.setToIdentity();  //------------------------------------

    matrix.translate(0, 0, -2);
    matrix.perspective(60, ((float)w)/((float)h), 0.1, 100.0);

    updateGL();
}

person Brokensoul .    schedule 08.03.2013    source источник
comment
Почему вы устанавливаете единое значение, прежде чем получить единое местоположение?   -  person genpfault    schedule 09.03.2013
comment
Э-э, я взял часть кода из единственного учебника, который работал на моем старом оборудовании. Тогда должно быть наоборот?   -  person Brokensoul .    schedule 09.03.2013
comment
да. Подумайте об этом: каково значение m_matrixUniform перед вызовом m_matrixUniform = m_program->uniformLocation("matrix")?   -  person genpfault    schedule 09.03.2013
comment
Неопределенный, я бы сказал. У меня было ощущение, что в этом есть что-то подозрительное.   -  person Brokensoul .    schedule 09.03.2013
comment
Ага. Хотя в этом конкретном случае, если вы инициализируете нулем m_matrixUniform в своем конструкторе, это, вероятно, не будет проблемой, поскольку первое место юниформ обычно равно нулю.   -  person genpfault    schedule 09.03.2013
comment
Что происходит в resizeGL(), когда вы меняете порядок вызовов translate() и perspective()?   -  person genpfault    schedule 09.03.2013
comment
Ничего не меняется. translate() вообще не может быть, иначе куб исчезнет. И perspective() должен иметь свой первый аргумент angle равным нулю. Я думал, что установка матрицы на идентичность сбросила все.   -  person Brokensoul .    schedule 09.03.2013


Ответы (1)


Наконец нашел в чем дело.

Во-первых, матричные операции не должны быть в initializeGL(). Не совсем уверен, почему. Правильное (и разумное, если подумать) место находится в resizeGL(), так как оно вызывается где-то при рендеринге первого кадра.

Во-вторых, матричные умножения имеют определенный порядок: перспектива > перевод > поворот. Иначе глохнет.

Таким образом, правильный (или, по крайней мере, рабочий) код в resizeGL() выглядит так:

glViewport(startX, startY, width, height);    //resize the viewport
matrix.setToIdentity();             //reset the uniform value (matrix)
perspectiveMatrix.setToIdentity();   //reset the projection matrix
                                       //set new projection (aspect ratio)
perspectiveMatrix.perspective(angle, (float)width/(float)height, nearPlane, farPlane);

matrix *= perspectiveMatrix;    //apply transformations
matrix *= translationMatrix;
matrix *= rotationMatrix;

updateGL();                  //re-render
person Brokensoul .    schedule 09.03.2013