繁体   English   中英

我无法使用仅直接状态访问的顶点数组对象设置来显示我的 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

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

我正在尝试获得一个绝对简单的 DSA-only 三角形实现,但三角形没有出现,并且在使用 glUseProgram(0) 时,固定功能管道不会“返回服务”,因此其余的遗留物体也会消失。

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

初始化时:

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

每帧一次:

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

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

由于您不使用任何投影或变换矩阵,因此必须在标准化设备空间中指定顶点坐标。 归一化的设备空间是一个立方体,左、下、近为 (-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深度测试

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM