1

所以我正在做一个塔防游戏的短板。我与他们共享了一个构建,因此我可以检查一切是否在另一台主机上正常运行。

实际发生的情况是,虽然我这边的一切都完美呈现(在我的 mac/xcode + windows/visual studio 2012 上),但在我朋友这边,几何形状似乎搞砸了。我屏幕上的每个对象都由一个 VBO 表示,我每次都使用它来渲染到不同的位置。但似乎我的 VBO 具有从所有模型导入的所有几何图形。(因此塔和树在一边。)

结果如下:

在此处输入图像描述(我的电脑) 在此处输入图像描述(我朋友的电脑)

到目前为止,我已经设法在一定程度上调试了这个问题。我可以说这不是我导入模型的方式,因为我正在创建一个包含所有向量的 debug.txt 文件,然后我将它们作为 VBO 发送到 gpu,并且在两台计算机上它们输出相同的值。所以我他们的向量不会被内存问题或类似的东西弄乱。所以我想也许这就是我设置或渲染我的 VBO 的方式

最让我印象深刻的是为什么事情在我的电脑上运行,而它们不在我朋友的电脑上。我确定的一个区别是我的计算机是开发人员站(无论这意味着什么),而我朋友的计算机不是。

这是我的 VBO 加载函数和我的 VBO 绘图函数:我使用 glfw 创建窗口和上下文,并包含 glew 标题以启用一些较新的 opengl 函数。

void               G4::Renderer::LoadObject(
                                           G4::TILE_TYPES   aType,
                                           std::vector<float> &v_data,
                                           std::vector<float> &n_data,
                                           std::vector<float> &t_data,
                                           float scale,
                                           bool has_texture,
                                           unsigned int texture_id
                                           )
{

    unsigned int vertices_id, vertices_size, normals_id, texturecoordinates_id;

    vertices_size   = static_cast<unsigned int>(v_data.size());

    glGenBuffers(1, &vertices_id);
    glGenBuffers(1, &normals_id);

    //::->Vertex array buffer upload.
    glBindBuffer(GL_ARRAY_BUFFER, vertices_id);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*v_data.size(), &v_data.front(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    //::->Normal Array buffer upload.
    glBindBuffer(GL_ARRAY_BUFFER, normals_id);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*n_data.size(), &n_data.front(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);



    if (has_texture)
    {
        glGenBuffers(1, &texturecoordinates_id);

        glBindBuffer(GL_ARRAY_BUFFER, texturecoordinates_id);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*t_data.size(), &(t_data[0]), GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }


    this->vbos[aType].Update(vertices_id, vertices_size, normals_id, texture_id, texturecoordinates_id, scale, has_texture);



}

绘制代码:

void G4::Renderer::DrawUnit(G4::VBO aVBO, bool drawWithColor, float r, float g, float b, float a)
{
    bool model_has_texture = aVBO.HasTexture();

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    if (model_has_texture && !drawWithColor)  {
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glEnable(GL_TEXTURE_2D);
    }

    if (drawWithColor)
    {
        glColor4f(r, g, b, a);
    }

    glScalef(aVBO.GetScaleValue(), aVBO.GetScaleValue(), aVBO.GetScaleValue());

    glBindBuffer(GL_ARRAY_BUFFER, aVBO.GetVerticesID());
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ARRAY_BUFFER, aVBO.GetNormalsID());
    glNormalPointer(GL_FLOAT, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);


    if (model_has_texture && !drawWithColor)
    {
        glBindTexture(GL_TEXTURE_2D, aVBO.GetTextureID());
        glBindBuffer(GL_ARRAY_BUFFER, aVBO.GetTextureCoordsID());
        glTexCoordPointer(2, GL_FLOAT, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    glDrawArrays(GL_TRIANGLES, 0, aVBO.GetVerticesSize());

    if (model_has_texture && !drawWithColor)  {
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisable(GL_TEXTURE_2D);

    } 

    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

}

我没有想法,希望有人可以指导我如何进一步调试。

4

2 回答 2

6

OpenGL 规范。没有指定当您发出具有比缓冲存储更多的顶点的绘图调用时应该发生的确切行为。这可能在一台机器上正常工作而不在另一台机器上正常工作的原因归结为实施。如果发生这种情况,每个供应商都可以自由地做他们想做的任何事情,因此渲染工件可能会出现在 AMD 硬件上,但根本不会出现在 nVIDIA 或 Intel 上。更糟糕的是,glDrawArrays (...)当要求绘制太多顶点时,调用实际上不会产生错误状态。您肯定需要在来自多个供应商的硬件上测试您的软件,以捕捉这些错误;谁在您的计算机中制造 GPU,以及驱动程序版本,与操作系统和编译器一样重要。

尽管如此,还是有办法捕捉这些愚蠢的错误。gDEBugger 就是其中之一,还有一个新的 OpenGL 扩展,我将在下面讨论。我更喜欢使用新的扩展,因为根据我的经验,除了不推荐使用的 API 调用和错误(gDEBugger 可以配置为监控)之外,扩展还可以警告您使用低效对齐的数据结构以及各种其他可移植性和性能问题。

我想添加一些我用来在我的软件中使用 OpenGL 调试输出的代码,因为这是一个错误行为的示例,它实际上不会生成可以用glGetError (...). 有时,您可以使用调试输出来捕捉这些错误(不过,我只是对其进行了测试,这不是其中一种情况)。您将需要一个 OpenGL 调试上下文才能使其工作(设置过程高度依赖于平台),但它是一个上下文标志,就像向前/向后兼容和核心一样(glfw 应该让您更容易)。

x86 平台的自动断点宏


// Breakpoints that should ALWAYS trigger (EVEN IN RELEASE BUILDS) [x86]!
#ifdef _MSC_VER
# define eTB_CriticalBreakPoint() if (IsDebuggerPresent ()) __debugbreak ();
#else
# define eTB_CriticalBreakPoint() asm (" int $3");
#endif


启用 OpenGL 调试输出(需要调试上下文和相对较新的驱动程序,OpenGL 4.x 时代)


// SUPER VERBOSE DEBUGGING!
if (glDebugMessageControlARB != NULL) {
  glEnable                  (GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
  glDebugMessageControlARB  (GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
  glDebugMessageCallbackARB ((GLDEBUGPROCARB)ETB_GL_ERROR_CALLBACK, NULL);
}


一些重要的实用函数,用更有意义的文本替换枚举值


const char*
ETB_GL_DEBUG_SOURCE_STR (GLenum source)
{
  static const char* sources [] = {
    "API",   "Window System", "Shader Compiler", "Third Party", "Application",
    "Other", "Unknown"
  };

  int str_idx =
    min ( source - GL_DEBUG_SOURCE_API,
            sizeof (sources) / sizeof (const char *) );

  return sources [str_idx];
}

const char*
ETB_GL_DEBUG_TYPE_STR (GLenum type)
{
  static const char* types [] = {
    "Error",       "Deprecated Behavior", "Undefined Behavior", "Portability",
    "Performance", "Other",               "Unknown"
  };

  int str_idx =
    min ( type - GL_DEBUG_TYPE_ERROR,
            sizeof (types) / sizeof (const char *) );

  return types [str_idx];
}

const char*
ETB_GL_DEBUG_SEVERITY_STR (GLenum severity)
{
  static const char* severities [] = {
    "High", "Medium", "Low", "Unknown"
  };

  int str_idx =
    min ( severity - GL_DEBUG_SEVERITY_HIGH,
            sizeof (severities) / sizeof (const char *) );

  return severities [str_idx];
}

DWORD
ETB_GL_DEBUG_SEVERITY_COLOR (GLenum severity)
{
  static DWORD severities [] = {
    0xff0000ff, // High (Red)
    0xff00ffff, // Med  (Yellow)
    0xff00ff00, // Low  (Green)
    0xffffffff  // ???  (White)
  };

  int col_idx =
    min ( severity - GL_DEBUG_SEVERITY_HIGH,
            sizeof (severities) / sizeof (DWORD) );

  return severities [col_idx];
}


我的调试输出回调(有点乱,因为它在我的软件中以不同的颜色打印每个字段)


void
ETB_GL_ERROR_CALLBACK (GLenum        source,
                       GLenum        type,
                       GLuint        id,
                       GLenum        severity,
                       GLsizei       length,
                       const GLchar* message,
                       GLvoid*       userParam)
{
  eTB_ColorPrintf (0xff00ffff, "OpenGL Error:\n");
  eTB_ColorPrintf (0xff808080, "=============\n");

  eTB_ColorPrintf (0xff6060ff, " Object ID: ");
  eTB_ColorPrintf (0xff0080ff, "%d\n", id);

  eTB_ColorPrintf (0xff60ff60, " Severity:  ");
  eTB_ColorPrintf ( ETB_GL_DEBUG_SEVERITY_COLOR   (severity),
                      "%s\n",
                        ETB_GL_DEBUG_SEVERITY_STR (severity) );

  eTB_ColorPrintf (0xffddff80, " Type:      ");
  eTB_ColorPrintf (0xffccaa80, "%s\n", ETB_GL_DEBUG_TYPE_STR     (type));

  eTB_ColorPrintf (0xffddff80, " Source:    ");
  eTB_ColorPrintf (0xffccaa80, "%s\n", ETB_GL_DEBUG_SOURCE_STR   (source));

  eTB_ColorPrintf (0xffff6060, " Message:   ");
  eTB_ColorPrintf (0xff0000ff, "%s\n\n", message);

  // Force the console to flush its contents before executing a breakpoint
  eTB_FlushConsole ();

  // Trigger a breakpoint in gDEBugger...
  glFinish ();

  // Trigger a breakpoint in traditional debuggers...
  eTB_CriticalBreakPoint ();
}



由于我实际上无法让您的情况触发调试输出事件,因此我想我至少会展示一个我能够触发的事件的示例。这不是您可以用 捕获glGetError (...)的错误,或者根本就不是错误。但这肯定是一个绘图调用问题,如果不使用此扩展程序,您可能在项目期间完全没有注意到 :)

OpenGL 错误:
==============
 对象 ID:102
 严重性:中
 类型:性能
 资料来源:API
 消息:glDrawElements 使用元素索引类型“GL_UNSIGNED_BYTE”,这对于当前硬件配置不是最佳的;考虑改用“GL_UNSIGNED_SHORT”。
于 2013-08-05T20:35:59.960 回答
4

在与我的朋友进行进一步的调试和多次试用后,我设法找到了问题所在。这花了我整整两天的时间才弄清楚,实际上这只是一个愚蠢的错误。

glDrawArrays(GL_TRIANGLES, 0, aVBO.GetVerticesSize());

上面的代码没有得到顶点大小(作为点),而是作为存储在那里的浮点总数。所以一切都乘以 3。添加 /3 解决了它。

所以我假设总点数乘以 vbo 从存储在 gpu 上的其他 vbo 中“窃取”数据的 3 倍。(因此树模型堆栈到我的塔)。

我还不能弄清楚,并且想要一个答案,是为什么在我的计算机上一切都很好,但在其他计算机上却没有。正如我在最初的问题中所说的那样,一个提示是我的计算机实际上是一个开发人员站,而我的朋友不是。任何愿意解释为什么这种效果不会在我身上重现的人,我都会很乐意接受他的回答作为我问题的解决方案。

谢谢

于 2013-08-05T19:29:49.543 回答