Reputation: 13
I'm trying to draw a Sphere with basic shaders. But I'm unable to get complete sphere drawn.
Sphere Calculations
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));
}
}
Binding
// 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);
Drawing
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, (GLsizei)ia.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
Fragment Shader
#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;
}
Vertex Shader
#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;
}
What am i doing wrong here. Is it the calculations of the vertices or the order of it. Or have i something wrong with my shaders ? Would appericate any help. Thank you in advance.
Kind of Sphere:
Upvotes: 1
Views: 1665
Reputation: 51933
First some minor issues:
in shaders outputting shader value shoul dbe the last line of code
So in vertex the line:
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
And in Fragment the line:
color = u_Color;
Should be the last line of code (followed only by the } termination of main
function). This is a safety measure as some GL implementations might optimize out any code after it !!!
try to ignore the index buffer and just render vertexes as points
This way you can quickly decide if the problem is in vertexes and texture coordinates or in indices.
Now the main issue:
You are interleaving x,y,z
and u,v
in single VBO but I see only single glVertexAttribPointer call. That means You have 2 attributes packed into single VBO and telling your OpenGL just about one of them.
Also Your values passed to it are not matching your data. As you set no stride and 3 elements per vertex. So OpenGL do not set any u,v
coordinates for texture and instead passing vertex points as follows:
v0 = x0,y0,z0
v1 = u0,v0,x1
v2 = y1,z1,u1
v3 = v1,x2,y2
v4 = z2,u2,v2
v5 = x3,y3,z3
...
so only 1 vertex in every 5 points is correct and all others are distorted by mixxing the coordinates and texture coordinates in that pattern.
You can quickly check for this by simply commenting out the two lines of code:
// va.push_back(circum_rel);
// va.push_back(1.0f - layer_rel);
Which will convert your data to non interleaved one and should render OK (without textures).
Now to remedy your problem (leave the 2 lines above un-commented) I would change this:
GLuint v_attr_inx = 0;
glVertexAttribPointer(v_attr_inx, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(v_attr_inx);
Into this:
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);
To match yours:
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec3 vertexColor;
However I would change the layout(location = 1) in vec3 vertexColor
into
layout(location = 1) in vec2 texcoord;
As you do not pass any 3D color and u,v
stands for 2D texture coords...
Hope I did not make any mistake in the glVertexAttribPointer
call as I do not use interleaving as all my GL engines have separate VBO per each attribute instead.
Pay attention to the first one and last two parameters. The first one should match the layout location you want to connect with. The last two are stride
and offset
. The Doc says stride is in Bytes however offset is not declared there so I just assume its also in bytes. So the stride is how many bytes your VBO has per single entry ... in your case (x,y,z,u,v)
is 5 floats. The offset is telling where the attribute starts so vertexes are from 0
and texture coordinates are skipping first 3 floats ...
Also I would add the normal as that will emphasize the sphere shape much more than texture once you add lighting...
Upvotes: 0