Creating a 3D Sphere in Opengl Using Visual C++

OpenGL/C++ 3D sphere

The call to glDrawElements must look like this

    glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);

Also since the draw method is written inline, no SolidSphere:: must prefix it (that would be valid only if the method is defined outside the class.

The code you posted originates in this StackOverflow answer I gave over a year ago: https://stackoverflow.com/a/5989676/524368

And if you look at this answer the definition of draw and the glDrawElements call therein are correct and any edits to the code in the answer were to other parts, but not this. So wherever you took the code from, it got altered with mistakes.

Drawing Sphere in OpenGL without using gluSphere()?

One way you can do it is to start with a platonic solid with triangular sides - an octahedron, for example. Then, take each triangle and recursively break it up into smaller triangles, like so:

recursively drawn triangles

Once you have a sufficient amount of points, you normalize their vectors so that they are all a constant distance from the center of the solid. This causes the sides to bulge out into a shape that resembles a sphere, with increasing smoothness as you increase the number of points.

Normalization here means moving a point so that its angle in relation to another point is the same, but the distance between them is different.
Here's a two dimensional example.

Sample Image

A and B are 6 units apart. But suppose we want to find a point on line AB that's 12 units away from A.

Sample Image

We can say that C is the normalized form of B with respect to A, with distance 12. We can obtain C with code like this:

#returns a point collinear to A and B, a given distance away from A. 
function normalize(a, b, length):
#get the distance between a and b along the x and y axes
dx = b.x - a.x
dy = b.y - a.y
#right now, sqrt(dx^2 + dy^2) = distance(a,b).
#we want to modify them so that sqrt(dx^2 + dy^2) = the given length.
dx = dx * length / distance(a,b)
dy = dy * length / distance(a,b)
point c = new point
c.x = a.x + dx
c.y = a.y + dy
return c

If we do this normalization process on a lot of points, all with respect to the same point A and with the same distance R, then the normalized points will all lie on the arc of a circle with center A and radius R.

bulging line segment

Here, the black points begin on a line and "bulge out" into an arc.

This process can be extended into three dimensions, in which case you get a sphere rather than a circle. Just add a dz component to the normalize function.

normalized polygons

level 1 bulging octahedron
level 3 bulging octahedron

If you look at the sphere at Epcot, you can sort of see this technique at work. it's a dodecahedron with bulged-out faces to make it look rounder.

calculating a sphere in opengl

Copy and Pasting some code I originally wrote in Creating a 3D sphere in Opengl using Visual C++

class SolidSphere
{
protected
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;

public:
void SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;

sphere_vertices.resize(rings * sectors * 3);
sphere_normals.resize(rings * sectors * 3);
sphere_texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = sphere_vertices.begin();
std::vector<GLfloat>::iterator n = sphere_normals.begin();
std::vector<GLfloat>::iterator t = sphere_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;
}

sphere_indices.resize(rings * sectors * 4);
std:vector<GLushort>::iterator i = sphere_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;
}
}

void draw(GLfloat x, GLfloat y, GLfloat z)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(x,y,z);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, &sphere_vertices[0]);
glNormalPointer(GL_FLOAT, 0, &sphere_normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &sphere_texcoords[0]);
glDrawElements(GL_QUADS, sphere_indices.size()/4, GL_UNSIGNED_SHORT, sphere_indices);
glPopMatrix();
}
}

how can I make them "curved"

You can't. All OpenGL primitives are "affine", i.e. planar or straight. Curvature is emulated by drawing short, straight sections with sufficient resolution.

Implementing opengl sphere example code

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++) {

Connecting Sphere Vertices OpenGL

Copy and Pasting for your reference some code I originally wrote in Creating a 3D sphere in Opengl using Visual C++

class SolidSphere
{
protected
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;

public:
void SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;

sphere_vertices.resize(rings * sectors * 3);
sphere_normals.resize(rings * sectors * 3);
sphere_texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = sphere_vertices.begin();
std::vector<GLfloat>::iterator n = sphere_normals.begin();
std::vector<GLfloat>::iterator t = sphere_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;
}

sphere_indices.resize(rings * sectors * 4);
std:vector<GLushort>::iterator i = sphere_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;
}
}
}


Related Topics



Leave a reply



Submit