OpenGL中如何使用GL_TRIANGLE_FAN画圆?
How to use GL_TRIANGLE_FAN to draw a circle in OpenGL?
四季问候大家!我修改了 this tutorial 中的代码以支持使用 VAO/VBO,但现在我明白了:
而不是漂亮的圆圈。这是代码:
#define GLEW_STATIC
#include <GLEW/glew.h>
#include <GLFW/glfw3.h>
#include <corecrt_math_defines.h>
#include <cmath>
#include <vector>
#define SCREEN_WIDTH 3000
#define SCREEN_HEIGHT 1900
void drawPolygon(GLuint& vao, GLuint& vbo, GLfloat x,
GLfloat y, GLdouble radius, GLint numberOfSides);
int main(void) {
if (!glfwInit()) {
return -1;
}
GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH,
SCREEN_HEIGHT, "Hello World", NULL, NULL);
if (!window) {
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glGetError();
glViewport(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT);
GLuint vao, vbo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, -1, 1);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
drawPolygon(vao, vbo, SCREEN_WIDTH / 2,
SCREEN_HEIGHT / 2, 250.0f, 50);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
void drawPolygon(GLuint& vao, GLuint& vbo, GLfloat x,
GLfloat y, GLdouble radius, GLint numberOfSides) {
int numVertices = numberOfSides + 2;
GLdouble twicePi = 2.0f * M_PI;
vector<GLdouble> circleVerticesX;
vector<GLdouble> circleVerticesY;
circleVerticesX.push_back(x);
circleVerticesY.push_back(y);
for (int i = 1; i < numVertices; i++) {
circleVerticesX.push_back(x + (radius *
cos(i * twicePi / numberOfSides)));
circleVerticesY.push_back(y + (radius *
sin(i * twicePi / numberOfSides)));
}
vector<GLdouble> vertices;
for (int i = 0; i < numVertices; i++) {
vertices.push_back(circleVerticesX[i]);
vertices.push_back(circleVerticesY[i]);
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(
GLdouble), vertices.data(), GL_STATIC_DRAW);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_DOUBLE, GL_FALSE,
2 * sizeof(GLdouble), (void*)0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 27);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
我到底做错了什么?!使用 the original code 有效,所以我对这个荒谬的结果感到非常困惑! MTIA 致任何可以提供帮助的人:-)
您的 VBO 的大小是 numVertices * sizeof(GLdouble)
,它是顶点数据实际大小的一半(每个顶点有一个 x 和 一个 y 分量)。因此,您最终绘制的顶点数量是您的 VBO 实际拥有的顶点数据的两倍。读取你的 VBO 的边界似乎会导致你的 OpenGL 实现中只有零,这就是为什么你的圆的下半部分的所有顶点都只是左下角(除非你明确启用健壮的缓冲区访问,否则不能保证这一点,只是你的驱动程序和 GPU 似乎在做什么)…
一些注意事项:
- 您通常不想使用
double
除非您需要它。 double
占用内存带宽的两倍,double
上的算术通常至少比 float
慢一点(是的,即使在 x86 CPU 上也是如此,因为浮点算术不是现在真的不再使用 x87 FPU 了)。 GPU 特别适用于 float
算法。特别是在消费类 GPU 上,double
算法比 float
. 显着 (一个数量级)慢
- 为什么不简单地将顶点数据直接推入
vertices
,而不是先推入 circleVerticesX
和 circleVerticesY
,然后从那里将其复制到 vertices
?
您确切地知道将要生成多少个顶点。因此,无需在生成坐标的循环中动态增加顶点容器(除其他事项外,.push_back()
几乎肯定会阻止循环的矢量化)。在进入循环之前,我建议至少 .reserve()
相应数量的元素(假设这是一个 std::vector
)。就个人而言,我会通过
分配一个适当的固定大小的数组
auto vertex_data = std::unique_ptr<GLfloat[]> { new GLfloat[numVertices * 2] };
在这种情况下。
- 您实际上不需要中心点。由于圆是凸形,您可以简单地使用圆上的一个点作为扇形的中心顶点。
- 这不一定是画圆的最有效方法(很多长而细的三角形;更多 here)
- 您可能不希望每一帧都一次又一次地生成和上传顶点数据,除非它发生某些变化。
- 除此之外,您可能希望
glDrawArrays
调用绘制实际的顶点数,而不是总是 27
…
四季问候大家!我修改了 this tutorial 中的代码以支持使用 VAO/VBO,但现在我明白了:
而不是漂亮的圆圈。这是代码:
#define GLEW_STATIC
#include <GLEW/glew.h>
#include <GLFW/glfw3.h>
#include <corecrt_math_defines.h>
#include <cmath>
#include <vector>
#define SCREEN_WIDTH 3000
#define SCREEN_HEIGHT 1900
void drawPolygon(GLuint& vao, GLuint& vbo, GLfloat x,
GLfloat y, GLdouble radius, GLint numberOfSides);
int main(void) {
if (!glfwInit()) {
return -1;
}
GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH,
SCREEN_HEIGHT, "Hello World", NULL, NULL);
if (!window) {
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glGetError();
glViewport(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT);
GLuint vao, vbo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, -1, 1);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
drawPolygon(vao, vbo, SCREEN_WIDTH / 2,
SCREEN_HEIGHT / 2, 250.0f, 50);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
void drawPolygon(GLuint& vao, GLuint& vbo, GLfloat x,
GLfloat y, GLdouble radius, GLint numberOfSides) {
int numVertices = numberOfSides + 2;
GLdouble twicePi = 2.0f * M_PI;
vector<GLdouble> circleVerticesX;
vector<GLdouble> circleVerticesY;
circleVerticesX.push_back(x);
circleVerticesY.push_back(y);
for (int i = 1; i < numVertices; i++) {
circleVerticesX.push_back(x + (radius *
cos(i * twicePi / numberOfSides)));
circleVerticesY.push_back(y + (radius *
sin(i * twicePi / numberOfSides)));
}
vector<GLdouble> vertices;
for (int i = 0; i < numVertices; i++) {
vertices.push_back(circleVerticesX[i]);
vertices.push_back(circleVerticesY[i]);
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(
GLdouble), vertices.data(), GL_STATIC_DRAW);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_DOUBLE, GL_FALSE,
2 * sizeof(GLdouble), (void*)0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 27);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
我到底做错了什么?!使用 the original code 有效,所以我对这个荒谬的结果感到非常困惑! MTIA 致任何可以提供帮助的人:-)
您的 VBO 的大小是 numVertices * sizeof(GLdouble)
,它是顶点数据实际大小的一半(每个顶点有一个 x 和 一个 y 分量)。因此,您最终绘制的顶点数量是您的 VBO 实际拥有的顶点数据的两倍。读取你的 VBO 的边界似乎会导致你的 OpenGL 实现中只有零,这就是为什么你的圆的下半部分的所有顶点都只是左下角(除非你明确启用健壮的缓冲区访问,否则不能保证这一点,只是你的驱动程序和 GPU 似乎在做什么)…
一些注意事项:
- 您通常不想使用
double
除非您需要它。double
占用内存带宽的两倍,double
上的算术通常至少比float
慢一点(是的,即使在 x86 CPU 上也是如此,因为浮点算术不是现在真的不再使用 x87 FPU 了)。 GPU 特别适用于float
算法。特别是在消费类 GPU 上,double
算法比float
. 显着 (一个数量级)慢
- 为什么不简单地将顶点数据直接推入
vertices
,而不是先推入circleVerticesX
和circleVerticesY
,然后从那里将其复制到vertices
? 您确切地知道将要生成多少个顶点。因此,无需在生成坐标的循环中动态增加顶点容器(除其他事项外,
分配一个适当的固定大小的数组.push_back()
几乎肯定会阻止循环的矢量化)。在进入循环之前,我建议至少.reserve()
相应数量的元素(假设这是一个std::vector
)。就个人而言,我会通过auto vertex_data = std::unique_ptr<GLfloat[]> { new GLfloat[numVertices * 2] };
在这种情况下。
- 您实际上不需要中心点。由于圆是凸形,您可以简单地使用圆上的一个点作为扇形的中心顶点。
- 这不一定是画圆的最有效方法(很多长而细的三角形;更多 here)
- 您可能不希望每一帧都一次又一次地生成和上传顶点数据,除非它发生某些变化。
- 除此之外,您可能希望
glDrawArrays
调用绘制实际的顶点数,而不是总是27
…