【问题标题】:When I call glDrawElements, I am getting weird behaviour当我调用 glDrawElements 时,我的行为很奇怪
【发布时间】:2020-07-07 06:34:58
【问题描述】:

我正在编写一个基本的纹理批处理渲染器,但不知何故,它只渲染一个纹理而不是四个。下面的代码有什么问题吗?我找不到任何问题。我正在使用 GLEW 和 GLFW 进行窗口化。

VertexBuffer、VertexArray、IndexBuffer、Shader 和 Texture 类已经过测试并且可以正常工作。

void BatchRenderObjects(vector<ObjectInstance> texq, GLuint tex_id, Shader shader)
{
int current_index = 0;
int array_index = 0;
const size_t vb_size = 20 * (texq.size());
const size_t ib_size = 6 * texq.size();
const size_t texslot_size = 32;

VertexBuffer VBO(GL_ARRAY_BUFFER);
IndexBuffer IBO;
VertexArray VAO;

GLfloat *vertex_buffer = new GLfloat[vb_size];
GLuint *index_buffer = new GLuint[ib_size];

{
    unsigned int offset = 0;
    for (int i = 0; i < ib_size; i += 6)
    {
        index_buffer[i + 0] = 0 + offset;
        index_buffer[i + 1] = 1 + offset;
        index_buffer[i + 2] = 2 + offset;
        index_buffer[i + 3] = 2 + offset;
        index_buffer[i + 4] = 3 + offset;
        index_buffer[i + 5] = 0 + offset;

        offset += 4;
    }
}

{
    for (int i = 0; i < texq.size(); i++)
    {
        vertex_buffer[current_index + 0] = texq[i].coords[0];
        vertex_buffer[current_index + 1] = texq[i].coords[1];
        vertex_buffer[current_index + 2] = texq[i].coords[2];
        vertex_buffer[current_index + 3] = texq[i].tex_coords[0];
        vertex_buffer[current_index + 4] = texq[i].tex_coords[1];

        vertex_buffer[current_index + 5] = texq[i].coords[3];
        vertex_buffer[current_index + 6] = texq[i].coords[4];
        vertex_buffer[current_index + 7] = texq[i].coords[5];
        vertex_buffer[current_index + 8] = texq[i].tex_coords[2];
        vertex_buffer[current_index + 9] = texq[i].tex_coords[3];

        vertex_buffer[current_index + 10] = texq[i].coords[6];
        vertex_buffer[current_index + 11] = texq[i].coords[7];
        vertex_buffer[current_index + 12] = texq[i].coords[8];
        vertex_buffer[current_index + 13] = texq[i].tex_coords[4];
        vertex_buffer[current_index + 14] = texq[i].tex_coords[5];

        vertex_buffer[current_index + 15] = texq[i].coords[9];
        vertex_buffer[current_index + 16] = texq[i].coords[10];
        vertex_buffer[current_index + 17] = texq[i].coords[11];
        vertex_buffer[current_index + 18] = texq[i].tex_coords[6];
        vertex_buffer[current_index + 19] = texq[i].tex_coords[7];

        current_index = current_index + 20;
    }
}

// Setup vertex buffer, index buffer and vertex array
{
    VAO.Bind();
    VBO.BufferData(vb_size, vertex_buffer, GL_STATIC_DRAW);
    VBO.VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    VBO.VertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    IBO.BufferData(ib_size, index_buffer, GL_STATIC_DRAW);
    VAO.Unbind();
}

shader.Use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id);
glUniform1i(glGetUniformLocation(shader.GetProgramID(), "u_Texture"), 0);
VAO.Bind();
glDrawElements(GL_TRIANGLES, 6 * texq.size(), GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_TRIANGLES, 0, 24); 
VAO.Unbind();

return; 
}

这实际上是非常基本的。它从结构中生成顶点缓冲区和索引缓冲区,并在屏幕上绘制纹理。这是ObjectInstance 结构。

struct  ObjectInstance
{
    float coords[12]; 
    float tex_coords[8];
};

最后是我的 main 函数的 sn-p。

ObjectInstance t1 = { {-1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
            {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

ObjectInstance t2 = { {-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f},
                    {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

ObjectInstance t3 = { {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f},
                {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

ObjectInstance t4 = { {0.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
                {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

std::vector<ObjectInstance> test_vector;

test_vector.push_back(t1);
test_vector.push_back(t2);
test_vector.push_back(t3);
test_vector.push_back(t4);

我在游戏循环中使用适当的参数调用该函数。

最后是我的顶点和片段着色器。 顶点着色器:

#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;

out vec2 TexCoord;

void main()
{
    gl_Position = vec4(position, 1.0f);
    TexCoord = texCoord; 
}

片段着色器

#version 330 core

in vec2 TexCoord;
out vec4 color; // the color outputted

uniform sampler2D u_Texture;

void main()
{
    color = texture(u_Texture, TexCoord); 
}

不,我有一个要绘制 4 次的纹理。它只是我想在屏幕的不同坐标中绘制的单个纹理。但是,显然我只在左下角打印了其中一个。你知道我为什么会收到这个错误吗?

【问题讨论】:

  • @VictorGubin 首先,非常感谢您的好意。不,我有一个要绘制 4 次的纹理。它只是我想在屏幕的不同坐标中绘制的单个纹理。但是,显然我只在左下角渲染了一个。我必须在不同的坐标处渲染 4 个相同的纹理。你知道我为什么会出现这种奇怪的行为吗?谢谢:)
  • @VictorGubin 我解决了这个问题.. 非常感谢!问题是 vb_size 和 ib_size 分配给了不正确的大小。由于 OpenGL 需要一个字节大小,我不得不将值乘以 sizeof(GLfloat)

标签: c++ opengl opengl-3 opengl-4


【解决方案1】:

让我们从这段代码开始:

const size_t vb_size = 20 * (texq.size());of GLfloats
const size_t ib_size = 6 * texq.size();

不知道您的特定 Buffer Object 抽象类,但 GL 中的 glBufferData 期望大小以字节为单位,所以

VBO.BufferData(vb_size, ...);
IBO.BufferData(ib_size, ...);

将仅上传实际数据的 1/4(与您的四个对象中的第一个完全匹配)。

【讨论】:

    【解决方案2】:

    您的所有对象都具有相同的纹理坐标。 由于您正在编写批处理渲染器,因此您的所有对象都使用单个绘制调用渲染,这意味着单个纹理(至少,查看您的代码,这似乎是您的方法,因为您只绑定一个纹理并且有除了当前绑定的单个纹理之外,您的着色器中没有代码可以使用任何其他纹理)。

    所以我假设你有一个由四个不同图像组成的纹理? 如果是这种情况,您应该获得 4 个对象,但每个对象都显示该纹理中的所有四个图像。 要修复它,您必须像这样调整纹理坐标:

    ObjectInstance t1 = { {-1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
                {0.5f, 0.5f,  0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f}};
    
    ObjectInstance t2 = { {-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f},
                        {0.5, 1.0f,  0.5f, 0.5f, 0.0f, 0.5f, 0.0f, 1.0f}};
    

    等等。我没有检查过这些,但这应该是你的问题的原因。

    另一种选择是绑定多个纹理并为每个对象提供某种 id,您可以在片段着色器中访问该 id 以确定要使用的纹理单元。在这种情况下,您需要在设置代码和片段着色器中做更多工作。

    【讨论】:

    • 不,我有一个纹理要绘制 4 次。它只是我想在屏幕的不同坐标中绘制的单个纹理。但是,显然我只在左下角打印了其中一个。你知道我为什么会收到这个错误吗?
    • 我明白了,所以它不像你说的那样“渲染单个纹理”,它渲染单个对象。这很奇怪,因为我也找不到您的代码有任何问题。三角形绕组似乎没问题,所以剔除不是问题。传递给 DrawElements 的索引数量似乎也是正确的。与您的索引值本身相同。我建议使用renderdoc.org 之类的工具调试您的应用程序,看看您是否可以在那里找到任何东西。此外,如果您的 ClearColor 设置为黑色,请尝试更改它,以防丢失的对象也被渲染为黑色。
    • 我解决了这个问题。非常感谢!问题是 vb_size 和 ib_size 分配给了不正确的大小。由于 OpenGL 需要一个字节大小,我不得不将值乘以 sizeof(GLfloat)
    【解决方案3】:

    嘿,问题来了。

    const size_t vb_size = 20 * (texq.size());
    const size_t ib_size = 6 * texq.size();
    

    应该是

    const size_t vb_size = (20 * (texq.size())) * sizeof(GLfloat); 
    const size_t ib_size = (6 * texq.size()) * sizeof(GLfloat);
    

    这是因为 OpenGL 采用原始字节的大小。

    【讨论】:

    • 非常感谢!我其实有点困惑
    【解决方案4】:

    我遇到了问题。正是opengl接受了字节大小。正如塞缪尔指出的那样..

    嘿,问题来了。

    const size_t vb_size = 20 * (texq.size());
    const size_t ib_size = 6 * texq.size();
    

    应该是

    const size_t vb_size = (20 * (texq.size())) * sizeof(GLfloat); 
    const size_t ib_size = (6 * texq.size()) * sizeof(GLfloat);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-13
      • 2016-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多