Матрица проекции OpenGL

Я пытаюсь заставить работать базовую программу OpenGL. Я создал два метода, которые создают матрицу вида модели (на данный момент очень элементарную, она только перемещает мой объект по оси Z) и матрицу проекции.

Моя задача состоит в том, что, насколько я понимаю, в OpenGL моя матрица проекции должна размещать вершины с положительными Z-координатами на экране и те, с отрицательными координатами за экраном.

Но что я испытываю, так это то, что мне нужно переместить куб, который я рисую, в отрицательном направлении z, если я хочу, чтобы он был показан. Вы можете объяснить, почему это так? Где я не прав? Или в моем коде ошибка?

Я использую эту функцию для создания моей матрицы проекции:

void Perspective(float *a, float fov, float aspect, float zNear, float zFar)
{
    for(int i = 0; i < 16; i++)
        a[i] = 0.0f;

    float f = 1.0f/float(tan(fov / 2.0f * (M_PI / 180.0f)));

    a[0 + 4 * 0] = f / aspect;
    a[1 + 4 * 1] = f;
    a[2 + 4 * 2] = (zNear + zFar) / (zNear - zFar);
    a[2 + 4 * 3] = 2.0f * zNear *+ zFar / (zNear - zFar);
    a[3 + 4 * 2] = -1.0f;
}

И этот для ModelView (я передаю различные смещения, они колеблются около 0, а z около -2):

void Modelview(float *mv, float scale, float xOff, float yOff, float zOff)
{
    for(int i = 0; i < 16; i++)
        mv[i] = 0.0f;

    mv[0 + 4 * 0] = scale;
    mv[0 + 4 * 3] = xOff;
    mv[1 + 4 * 1] = scale;
    mv[1 + 4 * 3] = yOff;
    mv[2 + 4 * 2] = scale;
    mv[2 + 4 * 3] = zOff;
    mv[3 + 4 * 3] = 1.0f;
}

Обе матрицы правильно передаются в opengl, я вычисляю положение вершины по:

gl_Position = modelview * projection * vertex_position;

Вот весь код на тот случай, если кому-то понадобится:

main.h

#include <stdio.h>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>
#include <GL/glew.h>
#include <GL/glut.h>

#include "util.h"

GLuint positionBufferObject, program;
GLint projectionLoc, modelviewLoc, vertexLoc, colorLoc;
float zNear = 0.1f, zFar = 100.0f;
float projection[16], modelview[16];

const Vertex vertices[] =
{
    Vertex(
                Vector4f(0.25f,  0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(0.25f,  -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f,  0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f,  -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f,  -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f,  0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f)),
        Vertex(
                Vector4f(0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f)),

        Vertex(
                Vector4f(0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f)),

        Vertex(
                Vector4f(-0.25f, 0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)),

        Vertex(
                Vector4f(-0.25f, 0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f,  0.25f, 0.25f, 1.0f),
                ColorRGBA(0.5f, 0.5f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.5f, 0.5f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(0.25f, -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.5f, 0.5f, 0.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f, 0.25f, 0.25f, 1.0f),
                ColorRGBA(0.5f, 0.5f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(0.5f, 0.5f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.5f, 0.5f, 0.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(0.25f,  0.25f, 0.25f, 1.0f),
                ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, 0.25f, 0.25f, 1.0f),
                ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, 0.25f, 0.25f, 1.0f),
                ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, 0.25f, -0.25f, 1.0f),
                ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(0.25f, -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 1.0f, 1.0f)),

        Vertex(
                Vector4f(0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, -0.25f, -0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 1.0f, 1.0f)),
        Vertex(
                Vector4f(-0.25f, -0.25f, 0.25f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 1.0f, 1.0f)),

        Vertex(
                Vector4f(-1.0f, 0, -1.0f, 1.0f),
                ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(0.0f, 1.0, -1.0f, 1.0f),
                ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)),
        Vertex(
                Vector4f(1.0f, 0, -1.0f, 1.0f),
                ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f))
};

const float vertexData[] =
{
     0.0f,    0.5f, 0.0f, 1.0f,
     0.5f, -0.366f, 0.0f, 1.0f,
    -0.5f, -0.366f, 0.0f, 1.0f,
     1.0f,    0.0f, 0.0f, 1.0f,
     0.0f,    1.0f, 0.0f, 1.0f,
     0.0f,    0.0f, 1.0f, 1.0f,
};

std::string strVertexShader = "simple.vert";

std::string strFragmentShader = "simple.frag";

void init();

void display();

void resize(int w, int h);

void InitializeProgram();

void InitializeVertexBuffer();

void InitializeGlutCallbacks();

void Perspective(float *a, float fov, float aspect, float zNear, float zFar);

void Modelview(float *mv, float scale, float xOff, float yOff, float zOff);

void ComputePositionOffsets(float &fXOffset, float &fYOffset, float &fZOffset, float &scale);

void PrintMat4(float *mat);

main.cpp

#include "main.h"

int main(int argc, char **argv)
{
    glutInit(&argc, argv);

        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
        glutInitWindowSize(640, 480);
        glutInitWindowPosition(100, 100);
        glutCreateWindow("Test");

        GLenum res = glewInit();
        if(res != GLEW_OK)
        {
                fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
                return -1;
        }

        init();

        glutMainLoop();

        return 0;
}

void display()
{
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        float fXOffset = 0.0f, fYOffset = 0.0f, fZOffset = 0.0f, scale = 0.0f;
        ComputePositionOffsets(fXOffset, fYOffset, fZOffset, scale);

        Modelview(modelview, scale, fXOffset, fYOffset, -2.0f + fZOffset);

        glUseProgram(program);

        glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, &projection[0]);
        glUniformMatrix4fv(modelviewLoc, 1, GL_FALSE, &modelview[0]);

        glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);

        glEnableVertexAttribArray(vertexLoc);
        glEnableVertexAttribArray(colorLoc);

        glVertexAttribPointer(vertexLoc, sizeof(Vector4f)/sizeof(float), GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
        glVertexAttribPointer(colorLoc, sizeof(ColorRGBA)/sizeof(float), GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(sizeof(Vertex)/2));

        glDrawArrays(GL_TRIANGLES, 0, sizeof(vertices)/sizeof(Vertex));

        glDisableVertexAttribArray(vertexLoc);
        glDisableVertexAttribArray(colorLoc);

        glUseProgram(0);

        glutSwapBuffers();
        glutPostRedisplay();
}

void resize(int w, int h)
{
        glViewport(0, 0, w, h);
        Perspective(projection, 90.0f, float(w)/float(h), zNear, zFar);
        PrintMat4(projection);
}

void InitializeProgram()
{
        std::vector<GLuint> shaderList;

        shaderList.push_back(CreateShader(GL_VERTEX_SHADER, strVertexShader));
        shaderList.push_back(CreateShader(GL_FRAGMENT_SHADER, strFragmentShader));

        program = CreateProgram(shaderList);

        vertexLoc = glGetAttribLocation(program, "vertex_position");
        colorLoc = glGetAttribLocation(program, "vertex_color");

        modelviewLoc = glGetUniformLocation(program, "modelview");
        projectionLoc = glGetUniformLocation(program, "projection");

        std::for_each(shaderList.begin(), shaderList.end(), glDeleteShader);
}

void InitializeVertexBuffer()
{
        glGenBuffers(1, &positionBufferObject);

        glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void InitializeGlutCallbacks()
{
        glutDisplayFunc(display);
        glutReshapeFunc(resize);
}

void init()
{
        InitializeProgram();
        InitializeVertexBuffer();

        InitializeGlutCallbacks();

        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        glFrontFace(GL_CW);

        glEnable(GL_DEPTH_TEST);
}

void Perspective(float *a, float fov, float aspect, float zNear, float zFar)
{
        for(int i = 0; i < 16; i++)
                a[i] = 0.0f;

        float f = 1.0f/float(tan(fov / 2.0f * (M_PI / 180.0f)));

        a[0 + 4 * 0] = f / aspect;
        a[1 + 4 * 1] = f;
        a[2 + 4 * 2] = (zNear + zFar) / (zNear - zFar);
        a[2 + 4 * 3] = 2.0f * zNear *+ zFar / (zNear - zFar);
        a[3 + 4 * 2] = -1.0f;
}

void Modelview(float *mv, float scale, float xOff, float yOff, float zOff)
{
        for(int i = 0; i < 16; i++)
                mv[i] = 0.0f;

        mv[0 + 4 * 0] = scale;
        mv[0 + 4 * 3] = xOff;
        mv[1 + 4 * 1] = scale;
        mv[1 + 4 * 3] = yOff;
        mv[2 + 4 * 2] = scale;
        mv[2 + 4 * 3] = zOff;
        mv[3 + 4 * 3] = 1.0f;
}

void ComputePositionOffsets(float &fXOffset, float &fYOffset, float &fZOffset, float &scale)
{
        float elapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
        float timeScale = 3.14159f * 2.0f;

        float xLoopDuration = 8.0f;
        float yLoopDuration = 3.0f;
        float zLoopDuration = 2.0f;
        float scaleLoopDuration = 10.0f;
        float xLoopProgress = fmodf(elapsedTime, xLoopDuration) / xLoopDuration;
        float yLoopProgress = fmodf(elapsedTime, yLoopDuration) / yLoopDuration;
        float zLoopProgress = fmodf(elapsedTime, zLoopDuration) / zLoopDuration;
        float scaleLoopProgress = fmodf(elapsedTime, scaleLoopDuration) /scaleLoopDuration;

        fXOffset = sinf(xLoopProgress * timeScale) * 0.5f;
        fYOffset = sinf(yLoopProgress * timeScale) * 0.5f;
        fZOffset = sinf(zLoopProgress * timeScale) * 0.5f;
        scale = 1/(1 + sinf(scaleLoopProgress * timeScale) * 0.5f);
}

void PrintMat4(float *mat)
{
        for(int i = 0; i < 4; i++)
        {
                for(int j = 0; j < 4; j++)
                {
                        std::cout << mat[j * 4 + i] << "\t";
                }
                std::cout << std::endl;
        }
        std::cout << std::endl;
}

3 ответа

Решение

Несколько вещей:

Во-первых, вы, кажется, устанавливаете "глаз" вашей проекционной матрицы на [0, 0 -1], толкая свою "камеру" к задней части экрана. Если это так, то вполне нормально, что вы должны переместить ваши объекты за пределы этой точки, чтобы они появились.

Виновная строка (я думаю, ваши имена переменных не совсем понятны:-/):

a[3 + 4 * 2] = -1.0f;

Я бы попытался изменить это на

a[3 + 4 * 2] = 1.0f;

и посмотреть, если это имеет значение.

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

glEnable(GL_CULL_FACE);

в

glDisable(GL_CULL_FACE);

и посмотрите, находится ли ваша "камера" внутри вашей сетки.

В-третьих, ваша сетка может находиться между вашей "камерой" и ближней плоскостью. Попробуйте поиграть с zNear и посмотрите, будет ли это иметь значение. zFar, вероятно, правильно, так как ваши объекты появляются, когда вы убираете их.

РЕДАКТИРОВАТЬ: Итак, я рассмотрел это более подробно и попытался выяснить вашу матрицу проекции. В соответствии с этим замечательным объяснением математики, лежащей в основе перспективных проекций, ваш код генерации матрицы проекции кажется правильным для генерации матрицы, которая смотрит вниз -Z. По сути, ваш код работает должным образом, поскольку OpenGL по умолчанию использует правую систему координат.

Если вы хотите иметь возможность смещать ваши объекты в +Z, потому что отрицательные значения раздражают вас, вы можете создать матрицу просмотра, которая выполняет поворот на 180 градусов вокруг оси Y, и умножить ее на матрицу проекции. Вы бы тогда получили

finalCoordinates = projection * view * model * vertex;

TL;DR: ваш код работает как положено.

Я не проверял ваш код, но вот один простой ответ: поскольку OpenGL использует так называемую правую систему координат, отрицательные значения z указывают направление "внутрь" экрана, тогда как положительное значение z означает "выход из экрана". " (т. е. используйте правую руку, направьте большой палец вправо -> положительную ось x. Ваш первый палец направлен вверх, прямой угол к большому пальцу -> положительная ось y. И последнее, но не менее важное: ваш средний палец, правая - под углом к ​​вашему первому пальцу и вашему удару, указывает на вас -> положительная ось z!)

Я нашел этот сайт очень полезным:

http://www.scratchapixel.com/lessons/3d-advanced-lessons/perspective-and-orthographic-projection-matrix/opengl-perspective-projection-matrix/

Это очень хорошо объясняет интересующую вас тему.

Другие вопросы по тегам