31. OpenGL Sphere Creation (Version 2.0)
Introduction
Creating spheres in OpenGL is a fundamental task for rendering objects like planets or balls in 3D space. While GLUT offers built-in methods for sphere generation, they are limited in customization, particularly when it comes to texture mapping. In this tutorial, we’ll learn how to create a textured sphere from scratch using custom calculations for vertices and texture coordinates.
By generating the sphere manually, we gain flexibility and control, allowing for applications like realistic Earth rendering, game objects, or scientific visualization.
Setting Up Variables
Before we can generate our sphere, we need to define the necessary variables. These include:
// Stores the sphere's texture GLuint texture[1]; // Angle of rotation for the sphere double angle = 0; // Vertex structure to store position and texture coordinates typedef struct { float X, Y, Z; // Vertex coordinates double U, V; // Texture coordinates } VERTICES; // Constants for calculations const double PI = 3.1415926535897; // Value of PI const int space = 10; // Angular step size for sphere segments const int VertexCount = (90 / space) * (360 / space) * 4; // Total vertices VERTICES VERTEX[VertexCount]; // Array to store sphere vertices
Creating the Sphere
The function `CreateSphere` is responsible for generating the vertices and texture coordinates of the sphere. It divides the sphere into latitude and longitude segments and calculates each point using trigonometric functions.
The function accepts four parameters:
- R: Radius of the sphere.
- H: Horizontal translation.
- K: Vertical translation.
- Z: Depth translation.
void CreateSphere(double R, double H, double K, double Z) { int n = 0; // Vertex counter for (double b = 0; b <= 90 - space; b += space) { for (double a = 0; a <= 360 - space; a += space) { // Vertex 1 VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b) / 180 * PI) - H; VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b) / 180 * PI) + K; VERTEX[n].Z = R * cos((b) / 180 * PI) - Z; VERTEX[n].U = (a) / 360; VERTEX[n].V = (2 * b) / 360; n++; // Vertex 2 VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b + space) / 180 * PI) - H; VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b + space) / 180 * PI) + K; VERTEX[n].Z = R * cos((b + space) / 180 * PI) - Z; VERTEX[n].U = (a) / 360; VERTEX[n].V = (2 * (b + space)) / 360; n++; // Vertex 3 VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b) / 180 * PI) - H; VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b) / 180 * PI) + K; VERTEX[n].Z = R * cos((b) / 180 * PI) - Z; VERTEX[n].U = (a + space) / 360; VERTEX[n].V = (2 * b) / 360; n++; // Vertex 4 VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b + space) / 180 * PI) - H; VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b + space) / 180 * PI) + K; VERTEX[n].Z = R * cos((b + space) / 180 * PI) - Z; VERTEX[n].U = (a + space) / 360; VERTEX[n].V = (2 * (b + space)) / 360; n++; } } }
Rendering the Sphere
To render the sphere, we use OpenGL's triangle strips and bind the texture to its surface. The `DisplaySphere` function handles this process:
void DisplaySphere(double R, GLuint texture) { glScalef(0.0125 * R, 0.0125 * R, 0.0125 * R); // Scale the sphere glRotatef(90, 1, 0, 0); // Rotate to correct orientation glBindTexture(GL_TEXTURE_2D, texture); // Bind the texture glBegin(GL_TRIANGLE_STRIP); for (int b = 0; b <= VertexCount; b++) { glTexCoord2f(VERTEX[b].U, VERTEX[b].V); glVertex3f(VERTEX[b].X, VERTEX[b].Y, -VERTEX[b].Z); } for (int b = 0; b <= VertexCount; b++) { glTexCoord2f(VERTEX[b].U, -VERTEX[b].V); glVertex3f(VERTEX[b].X, VERTEX[b].Y, VERTEX[b].Z); } glEnd(); }
Tutorial Code
Here is the full code for generating and rendering a textured sphere in OpenGL:
#include#include #include #include #include GLuint texture[1]; double angle = 0; typedef struct { float X, Y, Z; double U, V; } VERTICES; const double PI = 3.1415926535897; const int space = 10; const int VertexCount = (90 / space) * (360 / space) * 4; VERTICES VERTEX[VertexCount]; void CreateSphere(double R, double H, double K, double Z); void DisplaySphere(double R, GLuint texture); void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0, 0, -10); glRotatef(angle, 0, 1, 0); DisplaySphere(5, texture[0]); glutSwapBuffers(); angle++; } void init() { glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); texture[0] = LoadTextureRAW("texture.raw"); CreateSphere(70, 0, 0, 0); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(500, 500); glutCreateWindow("OpenGL Sphere"); init(); glutDisplayFunc(display); glutIdleFunc(display); glutMainLoop(); return 0; }
If you have any questions or encounter issues, feel free to email me at swiftless@gmail.com. Happy coding!
IanJ is right!
‘< VertexCount' instead of '<= VertexCount'.
Care of the 'glRotatef'. When I tried to get your code working in an already set model, it didn't work as I expected, first because of the 'glRotatef' But also because of the Z value of the second half sphere. Changing Z when creating the sphere (Z!=0) give two separate half sphere.
I fixed this with :
'glVertex3f (vertex[b].X, vertex[b].Y, vertex[b].Z – 2 * vertex[0].Z – 2 * (vertex[(int)VertexCount-1].Z – vertex[0].Z));' In the second displaying loop.
Not very clean, but at least, it works.
I also had to add
'
glEnd();
glFrontFace(GL_CW);
glBegin (GL_TRIANGLE_STRIP);
'
between the two rendering loop. Else, only one was correctly displayed. But this seems only to affect me. ??
Thank you a lot for this code, it saved me a lot of time 😀
(And could you correct in the original article, at least for the <= ? )
(sorry for my bad english)
Hi,
Is it possible to display only the triangular mesh without texture?
Best,
Siddharth
Use glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); to draw just the lines and not the texture.
const VertexCount = (90 / space) * (360 / space) * 4;
that line causes crashes, should be like
const VertexCount = (90 / space+1) * (360 / space+1) * 4;
also in drawing function there is pretty adventurous “<= VertexCount" as stated before in these comments
Impressive code! Took me just 5 minutes to compile with old version of lcc (had to switch the order of including windows.h and gl.h, a faq-ish issue), and it works great. Thanks a lot!
I have problem with the code..
i see alot of noises while the roitation…
myebe you know the reason
hey swiftless,
thanks for your tutorial .. but now i want to calculate normals for each vertex as i want to implement proper lighting and shading effects … can you give some light on which coordinates to calculate normal on ??
You’re a legend. God bless you.
I ported this to android opengl es in java, i expect there are bugs and missing function calls due to it being inherited from my mesh object, but you should be able to get the jist.
initially it only drew half a sphere, changing 90 to 180 fixed this, but im not sure if this is correct, if anyone could correct me on this i would be very greatful!!
public class Sphere extends mesh {
double PI = 3.1415926535897;
int space = 10;
int VertexCount = (180 / space) * (360 / space) * 4;
private FloatBuffer vertexBuffer = null;
private FloatBuffer textureBuffer = null;
public Sphere(float R, float H, float K, float Z) {
ByteBuffer vbb = ByteBuffer.allocateDirect(VertexCount * 3 * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
ByteBuffer byteBuf = ByteBuffer.allocateDirect(VertexCount * 2 * 4);
byteBuf.order(ByteOrder.nativeOrder());
textureBuffer = byteBuf.asFloatBuffer();
int n;
double a;
double b;
n = 0;
for( b = 0; b <= 180 – space; b+=space){
for( a = 0; a <= 360 – space; a+=space){
vertexBuffer.put((float)(R * Math.sin((a) / 180 * PI) * Math.sin((b) / 180 * PI) – H));
vertexBuffer.put((float)(R * Math.cos((a) / 180 * PI) * Math.sin((b) / 180 * PI) + K));
vertexBuffer.put((float)(R * Math.cos((b) / 180 * PI) – Z));
textureBuffer.put((float) ((a) / 360));
textureBuffer.put((float) ((2 * b) / 360));
vertexBuffer.put((float)(R * Math.sin((a) / 180 * PI) * Math.sin((b + space) / 180 * PI) – H));
vertexBuffer.put((float)(R * Math.cos((a) / 180 * PI) * Math.sin((b + space) / 180 * PI ) + K));
vertexBuffer.put((float)(R * Math.cos((b + space) / 180 * PI) – Z));
textureBuffer.put((float) ((a) / 360));
textureBuffer.put((float) ((2 * (b + space)) / 360));
vertexBuffer.put((float)(R * Math.sin((a + space) / 180 * PI) * Math.sin((b) / 180 * PI) – H));
vertexBuffer.put((float)(R * Math.cos((a + space) / 180 * PI) * Math.sin((b) / 180 * PI) + K));
vertexBuffer.put((float)(R * Math.cos((b) / 180 * PI) – Z));
textureBuffer.put((float) ((a + space) / 360));
textureBuffer.put((float) ((2 * b) / 360));
vertexBuffer.put((float)(R * Math.sin((a + space) / 180 * PI) * Math.sin((b + space) / 180 * PI) – H));
vertexBuffer.put((float)(R * Math.cos((a + space) / 180 * PI) * Math.sin((b + space) / 180 * PI) + K));
vertexBuffer.put((float)(R * Math.cos((b + space) / 180 * PI) – Z));
textureBuffer.put((float) ((a + space) / 360));
textureBuffer.put((float) ((2 * (b + space)) / 360));
}
}
vertexBuffer.position(0);
textureBuffer.position(0);
}
public void draw(GL10 gl) {
gl.glFrontFace(GL10.GL_CW);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
if (m_should_load_texture) {
loadGLTexture(gl);
m_should_load_texture = false;
}
if (m_texture_id != -1 && textureBuffer != null) {
gl.glEnable(GL10.GL_TEXTURE_2D);
// Enable the texture state
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Point to our buffers
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
gl.glBindTexture(GL10.GL_TEXTURE_2D, m_texture_id);
}
gl.glTranslatef(x, y, z);
gl.glRotatef(rx, 1, 0, 0);
gl.glRotatef(ry, 0, 1, 0);
gl.glRotatef(rz, 0, 0, 1);
// gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, VertexCount);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}
Can I get to see ur complete code ? for porting to Android
Thank you for the code. Took less than 5 minutes to get it working in my program including adding in the normals, not that they really took much effort either. Just what I needed so I could continue working with my project on noise textures.
Holy shit. Thanks a lot.
Other then a few compile errors, it runs beautifully.
how i can show a sphere in Iphone? As you know in ios there is no function as glVertices3f
Hi Milano,
I’d suggest taking a look at the OpenGL 4.x tutorials as they also can’t use the glVertex3f method. OpenGL ES and OpenGL 4.x have quite a few similarities.
Thanks,
Swiftless
Thanks for your great code! Very clean and clear. I realized the mapping theory through your code. Thousand thx!
Thanks a lot, It took a while to understand everything and to adapt for the iPhone but it works very well!
Thanks a lot!
Great, a triangle strip sphere with texture coordiante generation, just what I need to get these textured spheres working at a good speed.
Wait, this code is uncommented, uses obscure variable names and crams entire formulas onto one line. No problem, I am used to dealing with poorly written code. Ok, all rewritten properly, oh wait, the sphere looks like one of those spikey puff fishes. Setting “space” (angleIncrement for those of you who speak english) to 1, ok now it looks right, but my vertex count is astonomical. It’s ok, it’s using a triangle strip which is the fastest way to draw. Wait, no it’s not, regular triangles utilize the GPU cache better and are much faster. Going back to tesselization which is way better than this garbage.
Thanks for teaching me how to NOT draw a sphere.
Hey None,
Thanks for trying to tear me a new one, now it’s my turn 😀
The fact that you now have something working, thanks to my code, whereas before you couldn’t figure it out, means I must be doing something right. But no mention of that, thanks…
Now the code itself is over 5 years old, from back before I moved the site to WordPress, so no, it’s not the best and is in need of rewriting. But if you found problems with it being uncommented and obscure variable names, well then you both don’t know the maths behind a sphere (I explain the single letter variables in their relation to the maths equation) and haven’t picked up on the bigger errors, unlike others. Look at the vertex positions, I store them as ints in this tutorial, set them to floats or double for precision and fix up the buffer overrun which causes an extra undefined vertex. You didn’t mention these, so I’m assuming you are not as good a coder as you make out to be.
You’re angleIncrement doesn’t work for the values provided? You’ve done something wrong, no one else has mentioned it. Plus, a triangle strip is more efficient than plain triangles if you are working with large data sets because the number of vertices you have to push from system memory to GPU memory is decreased, often dramatically. And GPU algorithms already break all shapes back down to a triangle by default because of many of the optimization algorithms in place, so GPU caching has no effect in this instance (What is being cached in fixed function mode, you didn’t mention display lists being used?!).
Finally, you’re looking at the fixed function pipeline for speed? Sigh. When you eventually learn something about graphics rendering, you will move up to buffers. I’m betting you’re the type that still uses display lists for speed.
Good luck with your coding 😛
Cheers,
Swiftless
I think there’s a wee bug. DrawSphere() steps off the end of the array. The two loops should be terminated by “< VertexCount" not "<= VertexCount". If you don't get a crash, you get a weird "flangey thing" from the last seam to inside the centre of the sphere.
This is correct. If you keep it with “<=VertexCount" there's a thingy coming out of your sphere. All in all though, this is one of the best, if not THE best tutorial for drawing a sphere. I don't think it can be made any clearer than this!
Much appreciated Swiftless! I'll make sure to have a look at your other tutorials as well. If they are half as good as this, they'll be amazing!
Yes, the code is easy to record in a matter of short minutes. And I can imagine that a sphere of 70 subdivisions would yield about 25000 vertices? But I don’t exactli understand what takes place when the sine and cosine actions create each x,y,z,u and v vertex. Give a ‘slight’ synopsis of this.
Hi Hotcoffee Bennett,
In order to understand what the sine and cosine actions are doing, you need an understanding of trigonometry and how you can form shapes with trigonometric equations. I would recommend the following Wikipedia pages:
http://en.wikipedia.org/wiki/Trigonometric_function
http://en.wikipedia.org/wiki/Spherical_coordinates
Cheers,
Swiftless
Thanks alot for this tutorial!
I am programming on the PSP (Playstation Portable) and the PSPGU does not have any built in spheres like OpenGL has.
Thinking about the vertices of a sphere and how to display play them correctly with triangles made my head really hurt. 😛
Thanks alot. Not having texture coordinates as a cohort of my spherifical objects, and using GLUquadricsObj just seemed to cheap. Took about 20 minutes to really grasp. Appreciated.
Great tutorial! Very helpful.
For others: save this as a .cpp file, and compile using something like
g++ -lglut -lGL -lGLU sphere-ex.cpp -o sphere-ex
(that’s on Linux)
Is it possible to draw the sphere without doing the rotation. That is can you draw the sphere so it’s not sideways?
Hey Isak,
Yes it is possible and just involves a slight change in the code to create the sphere, I plan to fix this and many other problems with this tutorial at a later date.
Cheers,
Swiftless
hi swiftless i love the code!!!
i have one code to ask you, is it possible that i change the number of triangles to be used from 70 to around 180 or something in that range? if its possible what should i look into?
thanks a lot man! appreciate it!
Good Work. This code is very useful.
what is the meaning of those codes used in sphere creation? why to add space values to a and b each time? which method u r using here for sphere creation can u elaborate it?
I think what Shwetha was asking was “where would a sphere creation technique like this be used?”
Swiftless answers this in the description with: “Now you can make that solar system demo you have always wanted!”
But, there are some other uses for it as well.
-Simulating terrain for flight simulators
-Asteroids or space games
-Physics sims
chancy
Hi Shwetha,
If you look at the code supplied at the bottom of the tutorial, the sphere creation code is called in our init() method. This way we can set it up before we have to render it.
CreateSphere (double R, double H, double K, double Z)
Takes values for the size of the sphere (radius), and the H, K and Z refer to the location. I went with H, K and Z instead of X, Y and Z because of how I was taught spheres in maths.
Cheers,
Swiftless
what is usage of sphere creation and where it is used