更新
之前在知乎上发了一个帖子来询问,有个答主金条回答了而且还带了参考文档,原来在 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 才能绘制出图像。
如下图,当采用兼容 opengl 时,4 个三角形都绘制出来了。
可以通过注释 USE_OPENGL33
和 VAO_WITH_BO
来产生四种效果图。可以手动试试奥。
来源:oschina
链接:https://my.oschina.net/u/152535/blog/747328