我无法使用仅直接状态访问顶点数组对象设置来显示我的 OpenGL 4.5 "hello-world" 三角形

I cannot get my OpenGL 4.5 "hello-world" triangle to appear using direct-state-access-only vertex array object setup

我一直在清理一个使用 "old style" OpenGL 1.2 api 的应用程序,并希望升级该应用程序以使用 OpenGL 4.5,特别是使用直接使用无绑定状态管理状态访问,因为从理论上讲,它似乎可以更轻松地管理渲染的对象状态,同时减少对象之间的意外过度。

我正在尝试获得一个绝对简单的 DSA-only 三角形实现,但三角形没有出现,并且在使用 glUseProgram(0) 时,固定功能管道没有 "return to service",所以其余的遗留对象也消失了。

主应用程序创建三角形的两个实例 - 每个都有自己的顶点数组对象、程序对象、索引缓冲区和顶点位置缓冲区。当它运行时,程序对象具有相同的着色器代码,但部分练习是为了演示我可以以不影响应用程序中其他渲染对象的方式更改程序对象。这两个三角形实例的不同之处仅在于它们在实例化时接收到不同的值,该值用于在 X 中偏移三角形,因此两个三角形应该并排显示。在渲染周期中,一个循环值会在 Y 轴上上下移动三角形。视口使用 glViewport 在应用程序的主要部分设置,渲染是对默认帧缓冲区完成的。

初始化时:

  1. 着色器已编译和链接。顶点着色器采用单个位置属性
  2. 顶点位置数组已分配(但位置数据尚未上传)
  3. 创建索引数组并上传索引数据

每帧一次:

  1. 程序和顶点数组对象是used/bound
  2. 更新、上传顶点位置数据,并将缓冲区绑定到缓冲区绑定点(索引 0)
  3. 属性格式已设置并链接到该绑定索引
  4. 索引缓冲区使用 glVertexArrayElementBuffer 链接到 vao(不绑定到 GL_ELEMENT_ARRAY_BUFFER)
  5. 绘制命令已发出

我从来没有使用 glBindBuffer - 关键是我只尝试使用 DSA,我的[可能是错误的?] 信念是通过建立 vao 而未绑定仅使用 DSA 函数然后绑定它vao使用glBindVertexArray,所有的state都会变成"current"。请注意,这个故意天真的示例不使用投影矩阵或模型视图矩阵 - 它旨在直接在屏幕中间创建三角形,就像任何 "hello-world" 样式的第一个三角形应用程序一样。

我有一个通过 WGL 创建的经过验证的 4.5 GL 兼容性上下文、着色器编译和程序链接。我积极地进行防御性编码,并在每次调用后检查 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_);
}

由于您不使用任何投影或变换矩阵,因此必须在规范化设备中指定顶点坐标 space。归一化设备 space 是一个立方体,左、下、近为 (-1, -1, -1),右、上、远为 (1, 1, 1)。
不在该视域内的所有几何体都将被剪裁。

您的几何图形被裁剪了,因为所有 z 坐标都是 10.0f
更改 z 坐标(例如 0.0f)以解决问题。

注意glClearDepth 没有指定观察体积的远平面。它定义了当被 glClear(GL_DEPTH_BUFFER_BIT) 清除并且必须在 [0.0, 1.0] 范围内时写入深度缓冲区的值。默认为 1.0.
(参见 glDepthRange and Depth Test