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

Posted

技术标签:

【中文标题】我无法使用仅直接状态访问的顶点数组对象设置来显示我的 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 【发布时间】:2020-05-05 20:57:54 【问题描述】:

我一直在清理一个使用“旧式”OpenGL 1.2 api 的应用程序,并希望升级该应用程序以使用 OpenGL 4.5,特别是使用直接状态访问来使用无绑定状态管理,因为它看起来 - 在理论 - 它将更容易管理渲染的对象状态,减少对象之间的意外过度。

我正在尝试获得一个绝对的、仅 DSA 的三角形实现,但三角形没有显示出来,并且在使用 glUseProgram(0) 时,固定功能管道不会“返回服务”,所以其余的的遗留对象也消失了。

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

在初始化时:

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

每帧一次:

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

在任何时候我都不会使用 glBindBuffer - 关键是我试图仅使用 DSA,而我的 [可能是错误的?] 信念是,通过仅使用 DSA 函数在未绑定时建立 vao,然后使用绑定该 vao glBindVertexArray,所有状态都将变为“当前”。请注意,这个故意天真的示例不使用投影矩阵或模型视图矩阵 - 它旨在直接在屏幕中间创建三角形,就像任何“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_);

【问题讨论】:

我应该注意到 glClearDepth 和 glClearColor(等)实际上是在应用程序的主要部分完成的,但我将它们包含在此处以表明我没有完全省略它们) glClearDepth 的值必须在 [0.0, 1.0] 中。默认情况下它是 1,0,这很好。 【参考方案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。 (见glDepthRange和Depth Test)

【讨论】:

非常感谢,修改成功了!这突出了我对标准化设备坐标的理解上的不足——为此我也非常感激 :) 除了上面代码中的错误之外,我希望上面的示例对使用 DSA 的其他人有所帮助——我遇到了困难找到完整的例子。谢谢@Rabbid76!

以上是关于我无法使用仅直接状态访问的顶点数组对象设置来显示我的 OpenGL 4.5“hello-world”三角形的主要内容,如果未能解决你的问题,请参考以下文章

化解数据结构----图的遍历和应用

无法在 React 状态下设置对象数组

无法在 v-for 循环中显示数组中的所有对象,仅显示最后一个对象

无法捕捉 OpenGL 和顶点数组对象的错误

问题:为什么在我为孩子设置状态时,React为什么更新我的父母?仅发生在数组

在两个不同的顶点数组对象中使用顶点缓冲区