Trying to implement a sphere example code I found here on stackoverflow I have been running into problems. Note that I am not using the draw code, I am using the void SolidSphere function from this example to create my vertex data.
This is my code with some minor changes to the example that I refered to:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
Sphere(shared_ptr<GL::Shader> _shader, shared_ptr<Material> _material, float radius, unsigned int rings, unsigned int sectors)
{
this->attachShader(_shader);
this->attachMaterial(_material);
/** Implementation from:
https://stackoverflow.com/questions/5988686/how-do-i-create-a-3d-sphere-in-opengl-using-visual-c?lq=1
**/
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
//normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
//std::vector<GLfloat>::iterator n = sphere_normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) {
for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI/2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
//*n++ = x;
//*n++ = y;
//*n++ = z;
}
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings; r++) {
for(s = 0; s < sectors; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
}
/************* THIS IS THE ADDED PART ********/
for (unsigned int i = 0; i < vertices.size(); i += 3) {
vertexCoords.push_back(glm::vec4(vertices[i], vertices[i+1], vertices[i+2], 1));
}
for (unsigned int i = 0; i < texcoords.size(); i += 2) {
textureCoords.push_back(glm::vec2(texcoords[i], texcoords[i+1]));
}
indexElements = indices;
cout << "Vertex vector size: " << vertexCoords.size() << endl;
cout << "Texture vector size: " << textureCoords.size() << endl;
cout << "Element index vector size: " << indexElements.size() << endl;
}
The only big change here is using M_PI / 2 instead of M_PI_2 and two loops that pushes the value into the format my code uses which is:
vector <glm::vec4> vertexCoords;
vector <glm::vec2> textureCoords;
vector <GLushort> indexElements;
Here is a picture of what I am rendering:
so I am seeing this last line on the top of the sphere..
Is it anything obvious I am doing wrong in this code that could cause this?
In this loop:
for(r = 0; r < rings; r++) {
for(s = 0; s < sectors; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
}
you are accessing r+1 and s+1 which when r = rings - 1 and s = sectors - 1 (i.e. the last iteration of your loops) will be one higher than the data you have available.
This will be pointing to an uninitialised area of memory. This could contain anything, but in this case it appears to contain zeros which would explain the line going off to the origin.
As you are drawing the "next" quad you simply need to stop these loops one iteration earlier:
for(r = 0; r < rings-1; r++) {
for(s = 0; s < sectors-1; s++) {
You have only rings*sectors vertices but your indices go up to (rings-1+1)*sectors+(sectors-1+1)=rings*sectors+sectors. Which is exactly sectors number of vertices too much.
The problem is the output indices of bounds. Try to fix this part of the code so
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++) {
for(s = 0; s < sectors-1; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
}
Related
I'm trying to create a class that can procedurally create prisms (or cylinders if the precision is high enough) but only the sides of the 3d model are showing (not the top and bottom). This is using openGL and c++. Not going for efficiency, just modifying a previous class that made a sphere.
#define numSlices 2
Prism::Prism() {
init(3);
}
Prism::Prism(int prec) {
init(prec);
}
float Prism::toRadians(float degrees) { return (degrees * 2.0f * 3.14159f) / 360.0f; }
void Prism::init(int prec) {
prec = (prec < 3) ? 3 : prec;
numVertices = (prec + 1) * (numSlices+1);
numIndices = prec * numSlices * 6;
for (int i = 0; i < numVertices; i++) { vertices.push_back(glm::vec3()); }
for (int i = 0; i < numVertices; i++) { texCoords.push_back(glm::vec2()); }
for (int i = 0; i < numVertices; i++) { normals.push_back(glm::vec3()); }
for (int i = 0; i < numVertices; i++) { tangents.push_back(glm::vec3()); }
for (int i = 0; i < numIndices; i++) { indices.push_back(0); }
// calculate triangle vertices
for (int i = 0; i <= numSlices; i++) {
for (int j = 0; j <= prec; j++) {
float y = i;
float x = -(float)cos(toRadians(j * 360.0f / (float)prec));
float z = (float)sin(toRadians(j * 360.0f / (float)prec));
vertices[i * (prec + 1) + j] = glm::vec3(x, y, z);
texCoords[i * (prec + 1) + j] = glm::vec2(((float)j / prec), ((float)i / numSlices));
}
}
// calculate triangle indices
for (int i = 0; i < numSlices; i++) {
for (int j = 0; j < prec; j++) {
indices[6 * (i * prec + j) + 0] = i * (prec + 1) + j;
indices[6 * (i * prec + j) + 1] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 2] = (i + 1) * (prec + 1) + j;
indices[6 * (i * prec + j) + 3] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 4] = (i + 1) * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 5] = (i + 1) * (prec + 1) + j;
}
}
}
Any tips or solutions that stick closely to the code already written would much appreciated.
To render the top and bottom of the cylinder, you can create a "triangle fan" that starts from a vertex at the center of the top/bottom of the cylinder and creates one triangle for every side.
Adapting your code: (untested, I may have made mistakes against winding order)
int bottom_center = vertices.length(); vertices.push_back(glm::vec3(0,0,0));
int top_center = vertices.length(); vertices.push_back(glm::vec3(0,numSlices,0));
// Bottom
for (int j = 0; j < prec; j++) {
int base = 0;
indices.push_back(bottom_center);
indices.push_back(base+j);
indices.push_back(base+j+1);
}
// Top
for (int j = 0; j < prec; j++) {
int base = numSlices * (prec+1);
indices.push_back(top_center);
indices.push_back(base+j);
indices.push_back(base+j+1);
}
See http://www.songho.ca/opengl/gl_cylinder.html for a more worked-out example.
I want to modify the code for generating 3D sphere, so it uses triangles for drawing instead of quads. The problem is, as usual, that I get some error -- "vector iterator not incrementable". What's wrong with it?
SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1.0f / (float)(rings-1);
float const S = 1.0f / (float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const x = sinf(M_PI * r * R) * cosf(2 * M_PI * s * S);
float const y = sinf(-M_PI_2 + M_PI * r * R );
float const z = sinf(2.0f * M_PI * s * S) * sinf( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = x;
*n++ = y;
*n++ = z;
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++) for(s = 0; s < sectors-1; s++) {
/* triangles -- not working!
*i++ = r * sectors + s;
*i++ = (r + 1) * sectors + (s + 1);
*i++ = r * sectors + (s + 1);
*i++ = r * sectors + s;
*i++ = (r + 1) * sectors + s;
*i++ = (r + 1) * sectors + (s + 1); */
/* quads */
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
}
Looks like your triangle-generator-version of the loop does(sectors-1) times (rings-1) iterations, each one increasing i six times, but you have resized the vector i iterates through to just (rings * sectors * 4), which was enough to the quad-generator-version of the loop.
Assuming the triangle version was ok, this adjust shall fix it:
indices.resize(rings * sectors * 6)
This kind of oversight generally arises when you code without drinking enough coffee. Or too much of it (graph showing the hyperbola boundaries when your code will actually fail, (mapping rings and sectors numbers to x and y) due to the fact that you are allocating more space then needed for the iterations).
I'm looking for a good way to draw cylinder on opengl, i tried to draw multiple circles
for (GLuint m = 0; m <= segments; ++m) {
for (GLuint n = 0; n <= segments; ++n) {
GLfloat const t = 2 * M_PI * (float) n / (float) segments;
//position
points[num++] = x + sin(t) * r;
points[num++] = .0005 * m;
points[num++] = y + cos(t) * r;
//color
points[num++] = 1;
points[num++] = 1;
points[num++] = 1;
//texture
points[num++] = sin(t) * 0.5 + 0.5;
points[num++] = cos(t) * 0.5 + 0.5;
}
}
and on display function
GLuint pointer = 0;
for (GLuint i = 0; i <= segments; ++i) {
glDrawArrays(GL_TRIANGLE_FAN, pointer, segments + 1);
pointer += segments + 1;
}
I'm asking if there is a direct way to draw this cylinder
drawing many discs one on top of the other is too slow (unless you really want to draw the cylinder as slices of discs)
You should just draw the sides of the cylinder. For example a quad mesh would be
// for (GLuint m = 0; m <= segments; ++m)
float const bottom = .0005f * 0.f;
float const top = .0005f * (segments-1.f);
for(GLuint n = 0; n <= segments; ++n)
{
GLfloat const t0 = 2 * M_PI * (float)n / (float)segments;
GLfloat const t1 = 2 * M_PI * (float)(n+1) / (float)segments;
//quad vertex 0
points[num++] = x + sin(t0) * r;
points[num++] = bottom;
points[num++] = y + cos(t0) * r;
//quad vertex 1
points[num++] = x + sin(t1) * r;
points[num++] = bottom;
points[num++] = y + cos(t1) * r;
//quad vertex 2
points[num++] = x + sin(t1) * r;
points[num++] = top;
points[num++] = y + cos(t1) * r;
//quad vertex 3
points[num++] = x + sin(t0) * r;
points[num++] = top;
points[num++] = y + cos(t0) * r;
}
You can add 2 disks (the bases) to close the cylinder.
You can reduce fetching vertices form memory using a vertex+index buffer.
In new versions of OGL you can eliminate vertex memory read by indexing the mesh using gl_VertexID
I am modifying the excellent code provided in this answer Creating a 3D sphere in Opengl using Visual C++ to suit my needs, which is to draw it using GL_TRIANGLES and face culling like this:
GLCALL(glEnable(GL_CULL_FACE));
GLCALL(glCullFace(GL_BACK));
GLCALL(glFrontFace(GL_CCW));
I've managed to make the indices work for triangles, but I cant bloody figure out how to eliminate the cull face problem; as it is, it culls the wrong face using the indice setup I got below:
bool CreateSphereData(const float radius, const uint32_t rings, const uint32_t sectors, std::vector<float>& vertexData, std::vector<float>& normalData, std::vector<float>& texcoordData, std::vector<uint32_t>& indiceData)
{
if (radius <= 0 || rings <= 0 || sectors <= 0)
return false;
const float R = 1.0f / (float)(rings - 1);
const float S = 1.0f / (float)(sectors - 1);
const float pi = boost::math::constants::pi<float>();
const float pi_2 = pi / 2;
vertexData.resize(rings * sectors * 3);
normalData.resize(rings * sectors * 3);
texcoordData.resize(rings * sectors * 2);
auto v = vertexData.begin();
auto n = normalData.begin();
auto t = texcoordData.begin();
for (uint32_t r = 0; r < rings; r++)
{
for (uint32_t s = 0; s < sectors; s++)
{
const float y = sin(-pi_2 + pi * r * R);
const float x = cos(2 * pi * s * S) * sin(pi * r * R);
const float z = sin(2 * pi * s * S) * sin(pi * r * R);
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = x;
*n++ = y;
*n++ = z;
}
}
indiceData.resize(rings * sectors * 6);
auto i = indiceData.begin();
for (uint32_t r = 0; r < rings; r++)
{
for (uint32_t s = 0; s < sectors; s++)
{
*i++ = (r + 1) * sectors + s;
*i++ = r * sectors + s;
*i++ = r * sectors + (s + 1);
*i++ = (r + 1) * sectors + (s + 1);
*i++ = (r + 1) * sectors + s;
*i++ = r * sectors + (s + 1);
}
}
return true;
}
I am sure it is simple but I can't get it right. Any pointers?
EDIT/ANSWER:
Simply the following indices draws it correctly with setup mentioned above:
indiceData.resize(rings * sectors * 6);
auto i = indiceData.begin();
for (uint32_t r = 0; r < rings-1; r++)
{
for (uint32_t s = 0; s < sectors-1; s++)
{
*i++ = r * sectors + (s + 1);
*i++ = r * sectors + s;
*i++ = (r + 1) * sectors + s;
*i++ = r * sectors + (s + 1);
*i++ = (r + 1) * sectors + s;
*i++ = (r + 1) * sectors + (s + 1);
}
}
Heres a pic of the problem:
And here is the wireframe:
As you can see from the pictures above, I have some sort of weird graphical issue and am not sure how to fix, I think somethings wrong with the code although noone else has had trouble with the code.
Code:
unsigned int rings = 12, sectors = 24;
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * getR();
*v++ = y * getR();
*v++ = z * getR();
*n++ = x;
*n++ = y;
*n++ = z;
}
indices.resize(rings * sectors * 4);
std:vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices.data());
glNormalPointer(GL_FLOAT, 0, normals.data());
glTexCoordPointer(2, GL_FLOAT, 0, texcoords.data());
glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, indices.data());
Code taken from (Creating a 3D sphere in Opengl using Visual C++)
indices will end up with indexes outside of vertices.
The last four values in indices will be:
*i++ = 11 * 24 + 23 = 287;
*i++ = 11 * 24 + (23 + 1) = 288;
*i++ = (11 + 1) * 24 + (23 + 1) = 312;
*i++ = (11 + 1) * 24 + 23 = 311;
but vertices only contains 288 vertexes. I assume why it works for other people is that glDrawElements might wrap the indexes in some implementations.