【问题标题】:Modern OpenGL Sphere drawing with shaders带着色器的现代 OpenGL 球体绘图
【发布时间】:2021-08-07 08:10:51
【问题描述】:

我正在尝试使用基本着色器绘制一个球体。但我无法绘制完整的球体。

球体计算

    const float pi = 3.1414927f;
GLint layers = 100;
GLint circumferenceTiles = 100;
std::vector<float> va;
std::vector<int> ia;

// create the vertex attributes
va.reserve((layers + 1)* (circumferenceTiles + 1) * 5);  // 5 floats: x, y, z, u, v 
for (int il = 0; il <= layers; ++il)
{
    float layer_rel = (float)il / (float)layers;
    float layer_ang = (1.0f - 2.0f * layer_rel) * pi / 2.0f;
    float layer_sin = std::sin(layer_ang);
    float layer_cos = std::cos(layer_ang);
    for (int ic = 0; ic <= circumferenceTiles; ic++)
    {
        float circum_rel = (float)ic / (float)circumferenceTiles;
        float cricum_ang = circum_rel * 2.0f * pi - pi;
        float circum_sin = std::sin(cricum_ang);
        float circum_cos = std::cos(cricum_ang);

        va.push_back(layer_cos * circum_cos); // x
        va.push_back(layer_cos * circum_sin); // y
        va.push_back(layer_sin);              // z
        va.push_back(circum_rel);             // u
        va.push_back(1.0f - layer_rel);       // v
    }
}

// create the face indices 
ia.reserve(layers * circumferenceTiles * 6);
for (int il = 0; il < layers; ++il)
{
    for (int ic = 0; ic < circumferenceTiles; ic++)
    {
        int i0 = il * (circumferenceTiles + 1) + ic;
        int i1 = i0 + 1;
        int i3 = i0 + circumferenceTiles + 1;
        int i2 = i3 + 1;

        int faces[]{ i0, i1, i2, i0, i2, i3 };
        ia.insert(ia.end(), faces + (il == 0 ? 3 : 0), faces + (il == layers - 1 ? 3 : 6));
    }
}

绑定

    // Vertex Array
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// Vertex Buffer
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, va.size() * sizeof(*va.data()), va.data(), GL_STATIC_DRAW);

// Index Buffer
GLuint ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ia.size() * sizeof(*ia.data()), ia.data(), GL_STATIC_DRAW);

GLuint v_attr_inx = 0;
glVertexAttribPointer(v_attr_inx, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(v_attr_inx);

glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

绘图

        glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, (GLsizei)ia.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

片段着色器

#version 330 core

// Interpolated values from the vertex shaders
in vec3 fragmentColor;

// Ouput data
layout(location = 0) out vec4 color;
uniform vec4 u_Color;

void main(){

    // Output color = color specified in the vertex shader, 
    // interpolated between all 3 surrounding vertices
    color = u_Color;

}

顶点着色器

#version 330 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec3 vertexColor;

// Output data ; will be interpolated for each fragment.
out vec3 fragmentColor;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;

void main(){    

    // Output position of the vertex, in clip space : MVP * position
    gl_Position =  MVP * vec4(vertexPosition_modelspace,1);

    // The color of each vertex will be interpolated
    // to produce the color of each fragment
    fragmentColor = vertexColor;
}

我在这里做错了什么。是顶点的计算还是它的顺序。还是我的着色器有问题?会给予任何帮助。 提前谢谢你。

球体的种类:

【问题讨论】:

  • @Spektre 注释掉这两行效果很好。谢谢但我不明白如何解决这个问题。你能写出第二个 glVertexAttribPointer 函数吗?提前致谢
  • 我把 cmets 移到了答案中

标签: c++ opengl shader vbo


【解决方案1】:

首先是一些小问题:

  1. 在着色器中输出着色器值应该是代码的最后一行

    所以在顶点线:

    gl_Position =  MVP * vec4(vertexPosition_modelspace,1);
    

    在 Fragment 中的行:

    color = u_Color;
    

    应该是代码的最后一行(后面只有main 函数的} 终止符)。这是一种安全措施,因为某些 GL 实现可能会优化它之后的任何代码!!!

  2. 尝试忽略索引缓冲区,只将顶点渲染为点

    这样您可以快速确定问题出在顶点和纹理坐标中还是在索引中。

现在主要问题:

您在单个 VBO 中交错 x,y,zu,v,但我只看到单个 glVertexAttribPointer 调用。这意味着您将 2 个属性打包到单个 VBO 中,并仅告诉您的 OpenGL 其中一个。

您传递给它的值也与您的数据不匹配。当您没有设置步幅和每个顶点 3 个元素时。所以OpenGL不会为纹理设置任何u,v坐标,而是通过如下方式传递顶点:

v0 = x0,y0,z0
v1 = u0,v0,x1
v2 = y1,z1,u1
v3 = v1,x2,y2
v4 = z2,u2,v2 
v5 = x3,y3,z3
... 

因此,每 5 个点中只有 1 个顶点是正确的,而所有其他顶点都通过混合该模式中的坐标和纹理坐标而失真。

您可以通过简单地注释掉这两行代码来快速检查:

// va.push_back(circum_rel);
// va.push_back(1.0f - layer_rel);

这会将您的数据转换为非交错数据,并且应该可以渲染(没有纹理)。

现在要解决您的问题(保留上面的 2 行未注释)我会更改此:

GLuint v_attr_inx = 0;
glVertexAttribPointer(v_attr_inx, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(v_attr_inx);

进入这个:

GLuint v_attr_inx = 0;
glVertexAttribPointer(v_attr_inx, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), 0);
glEnableVertexAttribArray(v_attr_inx);

GLuint t_attr_inx = 1;
glVertexAttribPointer(t_attr_inx, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float)));
glEnableVertexAttribArray(t_attr_inx);

为了匹配你的:

layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec3 vertexColor;

但是我会将layout(location = 1) in vec3 vertexColor 更改为

layout(location = 1) in vec2 texcoord;

因为您不传递任何 3D 颜色,u,v 代表 2D 纹理坐标...

希望我在 glVertexAttribPointer 调用中没有犯任何错误,因为我不使用交错,因为我的所有 GL 引擎每个属性都有单独的 VBO。

注意第一个和最后两个参数。第一个应该与您要连接的布局位置相匹配。最后两个是strideoffset。文档说步幅以字节为单位,但是没有在此处声明偏移量,所以我只是假设它也是以字节为单位的。所以步幅是你的VBO每个条目有多少字节......在你的情况下(x,y,z,u,v)是5个浮点数。偏移量告诉属性从哪里开始,所以顶点来自0,纹理坐标跳过前 3 个浮点数 ...

我还会添加法线,因为一旦添加了光照,它会比纹理更强调球体形状...

【讨论】:

  • 感谢您的解释。只有“glVertexAttribPointer(t_attr_inx, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), 3*sizeof(float));”这部分代码需要像“glVertexAttribPointer(t_attr_inx, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float)));"不会出现编译器错误。开孔
猜你喜欢
  • 1970-01-01
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多