为什么简单的 opengl 代码却没有绘制出形状

陌路散爱 提交于 2020-10-28 10:19:14

更新

之前在知乎上发了一个帖子来询问,有个答主金条回答了而且还带了参考文档,原来在 specification 中有说明(好吧,看见那个一坨我就没有去翻了,后面有问题还是得去这里看看,说不定可以找到答案)。

概要

之前调试一个 opengl 小程序时,我写的临时代码无论如何都绘制不出形状来,检查了顶点数据 N 遍后确认顶点数据无误,于是我把注意点转向了 api 的调用,发现无法绘制出来形状和 opengl 版本是有些关系的。于是想写一篇 blog 整理一下。 opengl 历史就不说了,下面的测试代码用两种方式绘制三角形,一种是 opengl 现代模式绘制,另一种是兼容模式绘制。现代模式就是指定了 opengl 最低版本是 3.3 。

问题与结论

我遇见的问题如下。

  • 为什么我没有创建 vao 就绘制不出三角形?而在 opengles 中确可以。
  • 当我使用 vao 时,我可以不用 bo 来缓存顶点属性数据吗?
  • 如果我想在 opengl 中直接调用 glVertexAttribPointer 设置属性后调用 draw 函数绘制(调试时,总是想少写些代码),应该在什么环境下?

写代码测试后结论如下。

  • 指定 opengl 最低版本号为 3.3 时,这时绘制要创建一个 vertex array object (vao),否则无法绘制出形状。
  • 使用 vao 时,此时要使用 buffer object(bo)缓存顶点属性数据,否则无法绘制出形状。
  • 使用 vao 时,在 glBindVertexArray 后调用 glEnableVertexAttribArray 才会使 vao 内的顶点属性有效果。不然会导致顶点着色器无法接收数组属性数据。
  • 不指定 opengl 版本时,则没有太多要求,此时可以用最简单的代码来画一个三角形。

测试

为了弄清楚,我想了一个小例子来验证。例子中将绘制 4 个三角形。

  • 左上和右上是采用现代 opengl 绘制,这里左右的分别是左边采用了 bo 右边可根据宏定义是否采用 bo 。
  • 左下和右下是采用兼容 opengl 绘制。左采用了 bo 而右边未采用 bo 。并且绘制这两个三角形并未显示的创建一个 vao 。
  • 为了使现代模式和兼容模式互不影响,绘制时我采用了两个 program ,modern_program 和 compat_program 分别对应现代模式和兼容模式。

完整的代码在我的博客代码片段仓库中 blogsnippet\opengl\trimodern 目录。 下面定义了两个宏,默认是定义的,你可以注释掉来查看不同的效果。定义 USE_OPENGL33 表示采用现代 opengl 。定义 VAO_WITH_BO 表示采用 BO ,可以注释掉来查看三角形还能不能绘制。

#define USE_OPENGL33
#define VAO_WITH_BO

下面是绘制的代码,分成了两部分,首先用 modern_program 绘制上方的两个三角形,然后用 compat_program 绘制下方的两个三角形。

glUseProgram(modern_program);
glBindVertexArray(vao);
	glEnableVertexAttribArray(0); // 两个 program 都使用 0 索引属性,为了不被其他 program 影响,这里再次调用 
	// draw left up
	glBindBuffer(GL_ARRAY_BUFFER, leftup_bo);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // 在绘制时才定义数组属性数据
	glDrawArrays(GL_TRIANGLES, 0, 3);
	// draw right up
#ifdef VAO_WITH_BO
	glBindBuffer(GL_ARRAY_BUFFER, rightup_bo);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
#else
	glBindBuffer(GL_ARRAY_BUFFER, 0); // 测试代码,取消注释着两行,看看不使用 buffer object 能不能成功绘制
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices_rightup);
#endif
	glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glUseProgram(0);
		
glUseProgram(compat_program);
// draw left down
glBindBuffer(GL_ARRAY_BUFFER, leftdown_bo);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindBuffer(GL_ARRAY_BUFFER, 0); // 由于下一个绘制没有用到缓冲区,这里要解绑缓冲区,不然会受到影响
// draw right down
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices_rightdown);
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(0);

如下图。默认是宏都是定义的。可以发现采用 modern opengl 时,必须要显示创建 vao 才能绘制出图像。 use modern

如下图,当采用兼容 opengl 时,4 个三角形都绘制出来了。 use compat

可以通过注释 USE_OPENGL33VAO_WITH_BO 来产生四种效果图。可以手动试试奥。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!