Я не могу заставить свой треугольник приветствия OpenGL 4.5 появиться с помощью настройки объекта массива вершин с прямым доступом только к состоянию

Я отряхивал приложение, которое использует api OpenGL 1.2 в «старом стиле» и хочу обновить приложение для использования OpenGL 4.5 и, в частности, для использования управления состоянием без привязки с использованием прямого доступа к состоянию, потому что теоретически кажется, что это упростит управление состоянием визуализированного объекта с меньшим количеством случайных переходов между объектами.

Я пытаюсь получить абсолютно простую реализацию треугольника только для DSA, но треугольник не отображается, и при использовании glUseProgram (0) конвейер с фиксированной функциональностью не «возвращается в службу», поэтому остальная часть устаревшего объекты также исчезают.

Основное приложение создает два экземпляра треугольника - каждый получает свой собственный объект массива вершин, программный объект, буфер индекса и буфер положения вершины. В процессе работы программные объекты имеют идентичный шейдерный код, но часть упражнения состоит в том, чтобы продемонстрировать, что я могу изменять программные объекты таким образом, чтобы это не влияло на другие визуализированные объекты в приложении. Два экземпляра треугольника отличаются только тем, что они получают другое значение во время создания экземпляра, которое используется для смещения треугольника в X, поэтому два треугольника должны отображаться рядом. Во время цикла рендеринга циклическое значение перемещает треугольник вверх и вниз по оси Y. Окно просмотра устанавливается в основной части приложения с помощью glViewport, а рендеринг выполняется в буфер кадра по умолчанию.

Во время инициализации:

  1. Шейдеры скомпилированы и связаны. Вершинный шейдер принимает один атрибут позиции
  2. Выделен массив позиций вершин (но данные позиции еще не загружены)
  3. Индексный массив создан и индексные данные загружены

Один раз за кадр:

  1. Используются / привязаны программа и объект массива вершин
  2. Данные положения вершины обновляются, выгружаются, и буфер связывается с точкой привязки буфера (индекс 0)
  3. Формат атрибута устанавливается и связан с этим индексом привязки
  4. Индексный буфер связан с vao с помощью glVertexArrayElementBuffer (без привязки к GL_ELEMENT_ARRAY_BUFFER)
  5. Выдается команда рисования

Я ни в коем случае не использую glBindBuffer - дело в том, что я пытаюсь использовать только DSA, и мое [возможно ошибочное?] Убеждение состоит в том, что установив vao без привязки с использованием только функций DSA, а затем привязывая это vao с помощью glBindVertexArray, все состояние станет «текущим». Обратите внимание, что в этом намеренно наивном примере не используется матрица проекции или матрица модель-представление - он направлен на создание треугольников непосредственно в середине экрана, как и любое приложение первого треугольника в стиле «привет-мир».

У меня есть проверенный контекст совместимости 4.5 GL, созданный с помощью WGL, компиляции шейдеров и ссылок на программы. Я агрессивно кодировал защитно и проверял glGetError буквально после каждого вызова (хотя многие из них я вынул из кода для удобства чтения).

Сообщенная версия GL - "4.5.0 NVIDIA 382.05", а версия GLSL - "4.50 NVIDIA".

Я пробовал привязать индексный буфер к точке привязки буфера GL_ELEMENT_ARRAY_BUFFER, чтобы увидеть, имеет ли это значение, и это не работает. Я даже попытался привязать буфер позиции вершины к точке привязки GL_ARRAY_BUFFER, но (что не неожиданно) безрезультатно.

Либо мне не хватает чего-то простого, либо мое представление о том, как работает DSA, преувеличено и неверно. Что я могу попробовать дальше?

GlslTriangle.h:

#pragma once

#include <glad.h>

typedef unsigned short uint16;
typedef unsigned int uint32;

class GlslTriangle
{
private:
    int instance_{ 0 };

    GLuint prg_{ 0 };
    GLuint vao_{ 0 };
    GLuint vtx_{ 0 };
    GLuint idx_{ 0 };
    uint32 frameCount_{ 0 };

    GLuint CompileVtxShader();
    GLuint CompileFrgShader();
    void LinkProgram(GLuint v, GLuint f);
    void CreateBuffers();
    void ClearErr();
    void BreakIfErr();

public:

    GlslTriangle(int inst) :
        instance_(inst)
    {}

    void OneTimeInit();

    void Generate();

    void Render();

    void Deallocate();
};

GlglTriangle.cpp:

#include <stdafx.h>
using std::string;

#include <Geometry.h> //Point3f
#include <GlslTriangle.h>


void GlslTriangle::BreakIfErr()
{
    GLenum err;
    if ((err = glGetError()) != GL_NO_ERROR)
        assert(false);
}



void GlslTriangle::ClearErr()
{
    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR)
    {
    }
}



GLuint GlslTriangle::CompileVtxShader()
{
    auto v = glCreateShader(GL_VERTEX_SHADER);
    BreakIfErr();

    string vsrc =
        "#version 450 core \n"\
        "layout(location = 0) in vec3 vertexPosition; \n"\
        "void main() \n"\
        "{ \n"\
        "gl_Position.xyz = vertexPosition; \n"\
        "gl_Position.w = 1.0; \n"\
        "} \n";

    auto vsrca = vsrc.c_str();
    GLint vsrcl = vsrc.length();

    glShaderSource(v, 1, &vsrca, &vsrcl);
    BreakIfErr();

    glCompileShader(v);
    BreakIfErr();

    GLint vstatus{ 0 };
    glGetShaderiv(v, GL_COMPILE_STATUS, &vstatus);
    assert(vstatus);

    return v;
}



GLuint GlslTriangle::CompileFrgShader()
{
    auto f = glCreateShader(GL_FRAGMENT_SHADER);
    string fsrc =
        "#version 450 core \n" \
        "out vec3 color; \n "\
        "void main() \n" \
        "{ \n"\
        "color = vec3(0.5, 0.5, 1.0); \n"\
        "} \n";

    auto fsrca = fsrc.c_str();
    GLint fsrcl = fsrc.length();

    glShaderSource(f, 1, &fsrca, &fsrcl);
    BreakIfErr();

    glCompileShader(f);
    BreakIfErr();

    GLint fstatus{ 0 };
    glGetShaderiv(f, GL_COMPILE_STATUS, &fstatus);
    assert(fstatus);

    return f;
}



void GlslTriangle::LinkProgram(GLuint v, GLuint f)
{
    glAttachShader(prg_, v);
    glAttachShader(prg_, f);
    glLinkProgram(prg_);
    BreakIfErr();

    GLint lstatus{ 0 };
    glGetProgramiv(prg_, GL_LINK_STATUS, &lstatus);
    assert(lstatus);

    glDetachShader(prg_, v);
    glDetachShader(prg_, f);
    glDeleteShader(v);
    glDeleteShader(f);
}



void GlslTriangle::CreateBuffers()
{
    //Allocate space for 3 points - we'll populate data later
    glCreateBuffers(1, &vtx_);
    glNamedBufferStorage(vtx_, 3 * sizeof(Point3f), nullptr, GL_DYNAMIC_STORAGE_BIT);
    BreakIfErr();

    //Allocate space for 3 indices
    glCreateBuffers(1, &idx_);
    uint16 i[3];
    i[0] = 0;
    i[1] = 1;
    i[2] = 2;

    //Upload index data
    glNamedBufferStorage(idx_, 3 * sizeof(uint16), i, GL_DYNAMIC_STORAGE_BIT);
    BreakIfErr();
}



void GlslTriangle::OneTimeInit()
{
    ClearErr();

    glCreateVertexArrays(1, &vao_);

    prg_ = glCreateProgram();
    BreakIfErr();

    auto v = CompileVtxShader();
    auto f = CompileFrgShader();
    LinkProgram(v, f);

    CreateBuffers();
}



void GlslTriangle::Generate()
{
    ClearErr();

    //Provide a cyclic value that will push the triangle up and down in Y
    float cycle{ 1000.0f };
    float offset = 5 * sin(2*PI *(float)frameCount_ / cycle);

    //The instance parameter is provided at instantiation of "this" and
    //just offsets the triangle - with 2 instances of "this" we should see 
    //two triangles at different positions in X

    Point3f data[3];
    data[0] = { -1.0f + (float)instance_, 0.0f + offset, 10.0f};
    data[1] = { 0.0f + (float)instance_, 1.0f + offset, 10.0f};
    data[2] = { 1.0f + (float)instance_, 0.0f + offset, 10.0f};

    GLintptr bo{ 0 }; //buffer offset
    glNamedBufferSubData(vtx_, bo, 3 * sizeof(Point3f), data);
    BreakIfErr();

    ++frameCount_;
    frameCount_ = frameCount_ == cycle ? 0 : frameCount_;
}



void GlslTriangle::Render()
{
    GL::ClearErr();

    GLfloat skyColor[4] = { 0.75f, 0.75f, 1.0f, 1.0f };
    glClearColor(skyColor[0], skyColor[1], skyColor[2], skyColor[3]);
    glClearDepth(100.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(prg_);
    glBindVertexArray(vao_);
    glVertexArrayElementBuffer(vao_, idx_);

    GLuint a{ 0 }; //attribute idx
    GLuint b{ 0 }; //binding idx
    GLintptr offset{ 0 }; //offset in buffer
    GLint c{ 3 }; //components
    GLuint r{ 0 }; //relative offset in buffer element

    glVertexArrayAttribFormat(vao_, a, c, GL_FLOAT, GL_FALSE, r);
    glVertexArrayVertexBuffer(vao_, b, vtx_, offset, sizeof(Point3f));
    glVertexArrayAttribBinding(vao_, a, b);
    glEnableVertexArrayAttrib(vao_, a);

    GLsizei e{ 3 }; //count of elements
    glDrawElements(GL_TRIANGLES, e, GL_UNSIGNED_SHORT, nullptr);

    BreakIfErr();

    glUseProgram(0);
}



void GlslTriangle::Deallocate()
{
    glDeleteProgram(prg_);
    glDeleteVertexArrays(1, &vao_);
    glDeleteBuffers(1, &vtx_);
    glDeleteBuffers(1, &idx_);
}

person Smileyofoz    schedule 19.01.2020    source источник
comment
Я должен отметить, что glClearDepth и glClearColor (и т. Д.) Фактически выполняются в основной части приложения, но я включил их сюда, чтобы показать, что я не пропустил их полностью)   -  person Smileyofoz    schedule 19.01.2020
comment
Значение для glClearDepth должно быть в [0.0 , 1.0]. По умолчанию это 1,0, и это нормально.   -  person Rabbid76    schedule 19.01.2020


Ответы (1)


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

Ваша геометрия обрезана, потому что все координаты z равны 10.0f.
Измените координаты z (например, 0.0f), чтобы решить проблему.

Примечание. glClearDepth не указывает дальнюю плоскость объем просмотра. Он определяет значение, которое записывается в буфер глубины, когда он очищается с помощью glClear(GL_DEPTH_BUFFER_BIT), и должно быть в диапазоне [0.0, 1.0]. По умолчанию это 1.0.
(См. _5 _ и Проверка глубины)

person Rabbid76    schedule 19.01.2020
comment
Большое спасибо, что изменилось, сработало! Это выявило недостаток в моем понимании нормализованных координат устройства - и за это я также очень благодарен :) Помимо этой ошибки в моем коде выше, я надеюсь, что приведенный выше пример окажется полезным для других при использовании DSA - мне было трудно поиск полных примеров. Спасибо @ Rabbid76! - person Smileyofoz; 20.01.2020