在 OpenGL 4.0 中绘制球体

Posted

技术标签:

【中文标题】在 OpenGL 4.0 中绘制球体【英文标题】:draw sphere in OpenGL 4.0 【发布时间】:2017-03-29 07:56:29 【问题描述】:

我的 OpenGL 版本是 4.0。我想通过纬度和经度画一个球体。我用这个方法:

x=ρsinϕcosθ
y=ρsinϕsinθ
z=ρcosϕ

这是我的代码的一部分:

glm::vec3 buffer[1000];
glm::vec3 outer;
buffercount = 1000;
float section = 10.0f;
GLfloat  alpha, beta;
int index = 0;
for (alpha = 0.0 ; alpha <= PI; alpha += PI/section)

    for (beta = 0.0 ; beta <= 2* PI; beta += PI/section)
    

        outer.x = radius*cos(beta)*sin(alpha);
        outer.y = radius*sin(beta)*sin(alpha);
        outer.z = radius*cos(alpha);

        buffer[index] = outer;
        index = index +1;

    



GLuint sphereVBO, sphereVAO;

glGenVertexArrays(1, &sphereVAO);
glGenBuffers(1,&sphereVBO);
glBindVertexArray(sphereVAO);

glBindBuffer(GL_ARRAY_BUFFER,sphereVBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(glm::vec3) *buffercount  ,&buffer[0], GL_STATIC_DRAW);

glEnableVertexAttribArray(0);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
...
while (!glfwWindowShouldClose(window))

...
...

 for (GLuint i = 0; i < buffercount; i++)
    
        ...
        ...
        glm::mat4 model;
        model = glm::translate(model, buffer[i]);
        GLfloat angle = 10.0f * i;
        model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
        glUniformMatrix4fv(modelMat, 1, GL_FALSE, glm::value_ptr(model));
    


    glDrawArrays(GL_TRIANGLE_FAN, 0, 900);
    glfwSwapBuffers(window);

如果section = 5,表现是这样的:

如果section = 20。表现是这样的:

我认为我的代码中可能存在逻辑问题。我在这个问题上挣扎...

-----更新-----

我编辑了我的代码,它没有任何错误,但我得到了一个空白屏幕。我猜我的顶点着色器出了点问题。我可能会将错误的变量传递给顶点脱落器。请帮我。

gluperspective 在我的 OpenGL 4.1 中已弃用 我切换到:

float aspect=float(4.0f)/float(3.0f);
glm::mat4 projection_matrix =  glm::perspective(60.0f/aspect,aspect,0.1f,100.0f); 

它表明这个错误:常量表达式的计算结果为 -1,不能缩小到类型 'GLuint'(aka 'unsigned int')

GLuint sphere_vbo[4]=-1,-1,-1,-1;
GLuint sphere_vao[4]=-1,-1,-1,-1;

我不知道如何修改它...我切换到:

GLuint sphere_vbo[4]=1,1,1,1;
GLuint sphere_vao[4]=1,1,1,1;

我把Spektre的代码放在spherer.h文件里

这是我的 main.cpp 文件的一部分:

...
...
Shader shader("basic.vert", "basic.frag");

sphere_init();

while (!glfwWindowShouldClose(window))


    glfwPollEvents();

    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    shader.Use();

    GLuint MatrixID = glGetUniformLocation(shader.Program, "MVP");

    GLfloat radius = 10.0f;
    GLfloat camX = sin(glfwGetTime()) * radius;
    GLfloat camZ = cos(glfwGetTime()) * radius;

    // view matrix
    glm::mat4 view;
    view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));
    glm::mat4 view_matrix  = view;

    // projection matrix
    float aspect=float(4.0f)/float(3.0f);
    glm::mat4 projection_matrix = glm::perspective(60.0f/aspect,aspect,0.1f,100.0f);

    // model matrix
    glm::mat4 model_matrix = glm::mat4(1.0f);// identity

    //ModelViewProjection
    glm::mat4 model_view_projection = projection_matrix * view_matrix * model_matrix;

    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &model_view_projection[0][0]);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-10.0);

    glEnable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    sphere_draw();
    glFlush();
    glfwSwapBuffers(window);


sphere_exit();
glfwTerminate();
return 0;
 

这是我的顶点着色器文件:

#version 410 core 

uniform mat4 MVP;

layout(location = 0) in vec3 vertexPosition_modelspace;
out vec4 vertexColor;

void main()

    gl_Position =  MVP * vec4(vertexPosition_modelspace,1);
    vertexColor = vec4(0, 1, 0, 1.0);

我在我的 shader.h 文件中添加了错误检查功能 get_log

...
...
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
get_log(vertex);

...
...
void get_log(GLuint shader)

GLint isCompiled = 0;
    GLchar infoLog[1024];
    glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
    if(isCompiled == GL_FALSE)
    
        printf("----error--- \n");
        GLint maxLength = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
        glGetShaderInfoLog(shader, 1024, NULL, infoLog);
        std::cout << "| ERROR::::" << &infoLog << "\n| -- ------------------    --------------------------------- -- |" << std::endl;

        glDeleteShader(shader); // Don't leak the shader.

    else
        printf("---no error --- \n");
    


我测试了片段着色器和顶点着色器,都显示---没有错误---

【问题讨论】:

这看起来是对的,你的观点是正确的。只是你没有画出球体的表面,而是一个圆锥形螺旋线或一组在北极点的圆锥体填充了球体.问题是,buffer 是如何变成网格的? 我非常怀疑您是否可以绘制球形三角形扇形。由于每个三角形都包含北极顶点,因此这仅适用于第一个“环”,而不适用于所有其他“环”。 请查看***.com/q/5988686/3088138 以了解生成球体的 C++ 方式或将您的语言更改为 [C]。另请参阅***.com/a/8044252/3088138,了解GL_TRIANGLE_FAN 如何解释缓冲区数据,从而为您提供所见即所得的信息。 在每一层上使用 TRIANGLE_FAN ...它需要设置索引缓冲区来索引哪些点与三角形相连。执行此操作还有另一种选择:sphere triangulation by subdivision 或 Applying map of the earth texture a Sphere。此外,如果您渲染线框,您会更好地了解发生了什么,因为您不使用灯光和法线 .... 嗨@LutzL,谢谢你的建议。看起来glDrawElemen ts(GL_QUADS.. GL_QUADS 不支持我的 openGL 版本(我的版本是 4.1)。 【参考方案1】:

正如我在 cmets 中提到的,您需要向网格 VAO/VBO 添加索引。不知道为什么GL_QUADS 没有在您的机器上实现,这没有意义,因为它是基本的原语,所以为了使这个易于处理,我只使用GL_TRIANGLES,这远非理想,但到底是什么......试试这个:

//---------------------------------------------------------------------------
const int na=36;        // vertex grid size
const int nb=18;
const int na3=na*3;     // line in grid size
const int nn=nb*na3;    // whole grid size
GLfloat sphere_pos[nn]; // vertex
GLfloat sphere_nor[nn]; // normal
//GLfloat sphere_col[nn];   // color
GLuint  sphere_ix [na*(nb-1)*6];    // indices
GLuint sphere_vbo[4]=-1,-1,-1,-1;
GLuint sphere_vao[4]=-1,-1,-1,-1;

void sphere_init()
    
    // generate the sphere data
    GLfloat x,y,z,a,b,da,db,r=3.5;
    int ia,ib,ix,iy;
    da=2.0*M_PI/GLfloat(na);
    db=    M_PI/GLfloat(nb-1);
    // [Generate sphere point data]
    // spherical angles a,b covering whole sphere surface
    for (ix=0,b=-0.5*M_PI,ib=0;ib<nb;ib++,b+=db)
     for (a=0.0,ia=0;ia<na;ia++,a+=da,ix+=3)
        
        // unit sphere
        x=cos(b)*cos(a);
        y=cos(b)*sin(a);
        z=sin(b);
        sphere_pos[ix+0]=x*r;
        sphere_pos[ix+1]=y*r;
        sphere_pos[ix+2]=z*r;
        sphere_nor[ix+0]=x;
        sphere_nor[ix+1]=y;
        sphere_nor[ix+2]=z;
        
    // [Generate GL_TRIANGLE indices]
    for (ix=0,iy=0,ib=1;ib<nb;ib++)
        
        for (ia=1;ia<na;ia++,iy++)
            
            // first half of QUAD
            sphere_ix[ix]=iy;      ix++;
            sphere_ix[ix]=iy+1;    ix++;
            sphere_ix[ix]=iy+na;   ix++;
            // second half of QUAD
            sphere_ix[ix]=iy+na;   ix++;
            sphere_ix[ix]=iy+1;    ix++;
            sphere_ix[ix]=iy+na+1; ix++;
            
        // first half of QUAD
        sphere_ix[ix]=iy;       ix++;
        sphere_ix[ix]=iy+1-na;  ix++;
        sphere_ix[ix]=iy+na;    ix++;
        // second half of QUAD
        sphere_ix[ix]=iy+na;    ix++;
        sphere_ix[ix]=iy-na+1;  ix++;
        sphere_ix[ix]=iy+1;     ix++;
        iy++;
        
    // [VAO/VBO stuff]
    GLuint i;
    glGenVertexArrays(4,sphere_vao);
    glGenBuffers(4,sphere_vbo);
    glBindVertexArray(sphere_vao[0]);
    i=0; // vertex
    glBindBuffer(GL_ARRAY_BUFFER,sphere_vbo[i]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(sphere_pos),sphere_pos,GL_STATIC_DRAW);
    glEnableVertexAttribArray(i);
    glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,0,0);
    i=1; // indices
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,sphere_vbo[i]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(sphere_ix),sphere_ix,GL_STATIC_DRAW);
    glEnableVertexAttribArray(i);
    glVertexAttribPointer(i,4,GL_UNSIGNED_INT,GL_FALSE,0,0);
    i=2; // normal
    glBindBuffer(GL_ARRAY_BUFFER,sphere_vbo[i]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(sphere_nor),sphere_nor,GL_STATIC_DRAW);
    glEnableVertexAttribArray(i);
    glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,0,0);
/*
    i=3; // color
    glBindBuffer(GL_ARRAY_BUFFER,sphere_vbo[i]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(sphere_col),sphere_col,GL_STATIC_DRAW);
    glEnableVertexAttribArray(i);
    glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,0,0);
*/
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glDisableVertexAttribArray(3);
    
void sphere_exit()
    
    glDeleteVertexArrays(4,sphere_vao);
    glDeleteBuffers(4,sphere_vbo);
    
void sphere_draw()
    
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CCW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glBindVertexArray(sphere_vao[0]);
//  glDrawArrays(GL_POINTS,0,sizeof(sphere_pos)/sizeof(GLfloat));                   // POINTS ... no indices for debug
    glDrawElements(GL_TRIANGLES,sizeof(sphere_ix)/sizeof(GLuint),GL_UNSIGNED_INT,0);    // indices (choose just one line not both !!!)
    glBindVertexArray(0);
    

void gl_draw()
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    float aspect=float(xs)/float(ys);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0/aspect,aspect,0.1,100.0);
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-10.0);

    glEnable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    sphere_draw();

    glFlush();
    SwapBuffers(hdc);
    
//---------------------------------------------------------------------------

在创建 OpenGL 上下文并加载扩展程序调用 sphere_init() 之后,在关闭应用调用 sphere_exit() 之前使用很简单(当 OpenGL 上下文仍在运行时)以及当您想要渲染呼叫sphere_draw()。我用一些设置做了一个gl_draw() 示例,这里是它的预览:

重点是创建覆盖整个球体表面的 2D 点网格(通过球面长,纬度 a,b 角度),然后创建覆盖整个网格的三角形...

【讨论】:

嗨@spektre,我没有任何错误,但我得到了一个空白屏幕。我在我的问题中更新了我的代码,你能帮我看看吗?谢谢。 @Whatlahuhu 我认为这vertexColor = vec4(0, 1, 0, 1.0); 是你的问题尝试vertexColor = vec4(0.0, 1.0, 0.0, 1.0); 因为在GLSL 中禁止从int 自动转换为float,至少在我弄脏的实现中是禁止的。您应该始终检查 GLSL 编译和链接日志,如下所示:How to debug GLSL Fragment shader 特别是查找 glGetShaderInfoLog 用法 我仍然有一个空白屏幕。我正在尝试使用glGetShaderInfoLog @Whatlahuhu 我试过你的顶点,它看起来不错(所以他们一定已经用新的驱动程序改变了自动转换。唯一剩下的是错误的矩阵或片段着色器或 VBO 绑定。确保你的 VBO 与位置绑定到位置0(与着色器中相同)并尝试在关闭CULL FACE的情况下开始使用单位矩阵。如果渲染应该覆盖整个屏幕然后添加CULL FACE如果渲染添加投影并在相机之前平移矩阵并且仅然后添加其他内容,例如从欧拉角度查看 @Whatlahuhu 试试#version 410 core layout(location = 0) in vec3 vertexPosition_modelspace; out vec4 vertexColor; void main() gl_Position = vec4(vertexPosition_modelspace,1.0); vertexColor = vec4(0, 1, 0, 1.0); 我这边正在渲染,所以要么你遇到片段问题,要么矩阵错误或矩阵顺序......因为我不使用 GLM 我不知道它使用什么顺序.尝试不同的背景颜色,因为默认片段将在不使用旧样式颜色变量时使用黑色

以上是关于在 OpenGL 4.0 中绘制球体的主要内容,如果未能解决你的问题,请参考以下文章

OPENGL初学

OpenGL VBO:绘制球体

用 Opengl 画一个球体

OpenGL:球体减慢帧速率

在OpenGL中绘制一个相机对齐的圆

来自物体的 Opengl 闪电