1

我正在使用 gluTess* 函数来绘制非凸多边形。为了避免在每一步都重新进行镶嵌,我将结果存储在一个数组中,并使用 OpenGL 的顶点数组功能进行绘制。

我的问题是,由于某种原因,我第一次使用 gluTess* 时只绘制了部分三角形。如果我强制重新创建 gluTess*,那么一切都很好。

请注意,为了重新创建 gluTess*,我完全销毁包含顶点数组的对象并重新创建它(这会强制重新计算顶点数组对象)。

知道为什么会这样吗?

一些随机的想法:

  • 可能是因为对于第一个 OpenGL 调用,窗口还没有达到完整大小吗?
  • 我是否需要设置一些 OpenGL 状态,这将在稍后完成,但不是在第一次调用时完成?

谢谢。

编辑:作为测试,我刚刚创建、销毁并重新创建了顶点数组。它解决了问题。这意味着:在 OpenGL 状态没有任何变化的情况下,第一次调用 gluTess* 无法正确细分多边形。但是第二个成功了。以前有人注意到吗?

编辑(2):这是代码:

VA va;
GLUtesselator *t = gluNewTess();
gluTessCallback(t, GLU_TESS_BEGIN_DATA, (GLvoid (*)())beginVA);
gluTessCallback(t, GLU_TESS_END_DATA, (GLvoid (*)())endVA);
gluTessCallback(t, GLU_TESS_VERTEX_DATA, (GLvoid (*)())vertexVA);
gluTessCallback(t, GLU_TESS_ERROR, (GLvoid (*)())&tessError);
gluTessCallback(t, GLU_TESS_COMBINE, (GLvoid (*)())&tessCombine);

gluTessProperty(t, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
gluTessProperty(t, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);

gluTessBeginPolygon(t, &va);
gluTessBeginContour(t);
foreach(Point3d& p, points)
  gluTessVertex(t, const_cast<GLdouble*>(p.c_data()), (void*)&p);
gluTessEndContour(t);
gluTessEndPolygon(t);
gluDeleteTess(t);

随着第二次调用成功,我怀疑 beginVA、endVA 和 vertexVA 工作正常(正如我所说,第二次调用是通过破坏主要保存顶点数组的数据结构 VA 进行的)。

编辑(3):这是缺少的代码:

struct VA
{
  std::vector<Point3d> positions; // Vertex array
  GLenum current_mode; // Drawing mode (GL_TRIANGLE, *_FAN or *_STRIP)
  Point3d v1, v2; // Two last vertices for _FAN or _STRIP
  bool has_v1, has_v2; // did we get the two last vertices?
  int n; // Index of the vertex, for *_STRIP
};


void beginVA(GLenum mode, VA *va)
{
  va->current_mode = mode; // Store the mode
  va->has_v1 = va->has_v2 = false; // We haven't had any vertex yet
  va->n = 0;
}

void endVA(VA *va)
{
  va->current_mode = 0; // Not really necessary, but cleaner
}

void vertexVA(Point3d *p, VA *va)
{
  ++va->n;
  if(va->current_mode == GL_TRIANGLES) // The simple case
    va->positions.push_back(*p);
  else if(!va->has_v1) {
    va->v1 = *p;
    va->has_v1 = true;
  } else if(!va->has_v2) {
    va->v2 = *p;
    va->has_v2 = true;
  } else if(va->current_mode == GL_TRIANGLE_STRIP) {
    if(va->n%2 == 1) {
      va->positions.push_back(va->v1);
      va->positions.push_back(va->v2);
      va->positions.push_back(*p);
    } else {
      va->positions.push_back(va->v2);
      va->positions.push_back(va->v1);
      va->positions.push_back(*p);
    }
    va->v1 = va->v2;
    va->v2 = *p;
  } else { // GL_TRIANGLE_FAN
    va->positions.push_back(va->v1);
    va->positions.push_back(va->v2);
    va->positions.push_back(*p);
    va->v2 = *p;
  }
}

编辑(4):最后,错误出现在其他地方。我一定是累了,我使用了一个 std::vector 来存储 combine 函数的结果。我不知道为什么后来它起作用了,但是它肯定是正常的,它第一次不起作用!对不起,我现在关闭这个话题。

4

1 回答 1

2

OpenGL 和 GLU 是相互独立的。它们通常结合在一起,但 GLU 细分函数独立于 OpenGL 工作。

存储镶嵌的顶点数据是一件好事,应该这样做。

编辑:前两个问题编辑回答:

您是否尝试在不重新创建镶嵌数据的情况下发出重绘?

如果您显示相关代码及其上下文,那么给出一个好的和明智的答案会容易得多。

编辑:由第三个问题编辑回答:

下一个问题:beginVA、endVA、vertexVA的签名是什么?以及vertexVA如何将顶点插入到VA中。VA长什么样子?(类、结构定义等)。

我可以提供一个建议:

struct VA
{
  std::vector<Point3d> positions; // Vertex array
  std::vector<GLuint> triangle_face_indices;
  std::vector<GLuint> tristrip_face_indices;
  std::vector<GLuint> trifan_face_indices;
  GLenum current_mode; // Drawing mode (GL_TRIANGLE, *_FAN or *_STRIP)
};

void beginVA(GLenum mode, VA *va)
{
  va->current_mode = mode; // Store the mode
}

void endVA(VA *va)
{
  va->current_mode = 0; // Not really necessary, but cleaner
}

void vertexVA(void *p, VA *va)
{
  GLuint idx = (GLuint)((intptr_t) p);

  switch(va->current_mode) {
  case      GL_TRIANGLES: va->triangle_face_indices.push_back(i); break;
  case GL_TRIANGLE_STRIP: va->tristrip_face_indices.push_back(i); break;
  case   GL_TRIANGLE_FAN: va->trifan_face_indices.push_back(i); break;
  }
}

像这样使用

gluTessBeginPolygon(t, &va);
gluTessBeginContour(t);
for(GLuint i = 0; i < va.positions.size(); i++) {
  gluTessVertex(t, const_cast<GLdouble*>(va.positions[i].c_data()), (void*)((intptr_t)i));
}
gluTessEndContour(t);
gluTessEndPolygon(t);
gluDeleteTess(t);

并使用 3 次连续调用来glDrawElements绘制任一绘制模式。

于 2011-06-23T12:55:20.047 回答