31. OpenGL Sphere Creation
Fancy… Here we are going to create our own sphere without the use of
GLUT. I chose to write this tutorial, because I did not like the way
you could and could not texture the sphere created by GLUT and when
I looked on the internet, I could not find any tutorials on it. The advantage
of our sphere, is that we can set texture coordinates and if need be, place it
inside either a display list, vertex array, or vertex buffer object, allowing
us to call alot more than in immediate mode.
———————————————————————
Section 1: Variables
Now to start off, here are the variables we are going to be using:
This will hold our texture
GLuint texture[1];
This will just hold our angle of rotation
double angle = 0;
This here will hold the information of all our vertices, such as
x, y and z coordinates along with texture coordinates.
typedef struct
{
The x position of our current vertex
int X;
The Y position of our current vertex
int Y;
The Z position of our current vertex
int Z;
The U(x) texture coordinate of the current vertex
double U;
The V(x) texture coordinate of the current vertex
double V;
}VERTICES;
Next we are going to use PI to convert our angles in degrees to radians.
const double PI = 3.1415926535897;
This here is going to determine how far apart each of our vertices are
the further apart, the faster the program, but the squarer the sphere.
I chose 10 because it looks nice, and runs excellent.
const space = 10;
This will hold out total amount of vertices.
const VertexCount = (90 / space) * (360 / space) * 4;
Now we set up how many vertices we are going to use.
VERTICES VERTEX[VertexCount];
———————————————————————
Section 2: Enabling and Creating
Here I am enabling depth testing, texturing and face culling. The depth testing
is so that we acctually have depth to our scene, the texturing is so that
we can apply textures to our sphere, and culling to used to speed up the
application. I have set the front face for culling to Counter Clock Wise, as triangle
strips cull the opposite face to most other shapes.
glEnable(GL_DEPTH_TEST);
glEnable( GL_TEXTURE_2D );
glDepthFunc(GL_LEQUAL);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
Now we load our texture to be used. It will be stored in texture[0]
texture[0] = LoadTextureRAW( “texture.raw” );
Now we call to create the sphere, I do not know why it does this, but the first
input seems to choose how many subdivisions to perform. The next lets you choose
where to move the sphere on the x, y and z axis.
CreateSphere(70,0,0,0);
Now for the actual creation code. We are inputting R as the number of subdivisions,
H as the translation on the horizontal axis, K as the translation on the vertical
axis, and Z as the translation on the Z axis.
void CreateSphere (double R, double H, double K, double Z) {
Now are variables for this is as followed. n is the current vertex we are working
with. While a and b are used to control our loops.
int n;
double a;
double b;
Set n to 0 to start off with the first vertex
n = 0;
Assign our b loop to go through 90 degrees in intervals of our variable space
for( b = 0; b <= 90 – space; b+=space){
Assign our a loop to go through 360 degrees in intervals of our variable space
for( a = 0; a <= 360 – space; a+=space){
Start editing our vertex.
I am calculating the X value here.
VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b) / 180 * PI) – H;
The Y value here.
VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b) / 180 * PI) + K;
The Z value here.
VERTEX[n].Z = R * cos((b) / 180 * PI) – Z;
Now I am calculating the texture coordinates. I have used (2*b) as the texture
is twice as wide as it is high. Hence 2:1. You can remove the (2*) if you wish
to use a texture with the same width and height, or increase it accordingly.
VERTEX[n].V = (2 * b) / 360;
VERTEX[n].U = (a) / 360;
Then start working with the next vertex
n++;
Then we do the same calculations as before, only adding the space variable
to the b values.
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].V = (2 * (b + space)) / 360;
VERTEX[n].U = (a) / 360;
n++;
Then we do the same calculations as the first, only adding the space variable
to the a values.
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].V = (2 * b) / 360;
VERTEX[n].U = (a + space) / 360;
n++;
Then we do the same calculations as the first again, only adding the space variable
to both the b and the a values.
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].V = (2 * (b + space)) / 360;
VERTEX[n].U = (a + space) / 360;
n++;
}
}
}
———————————————————————————
Section 3: Displaying the Sphere
To display the sphere I am calling this funtion which will set the size of it
to 5 and assign the texture specified.
DisplaySphere(5, texture[0]);
This is the actual displaying code itself, it inputs the radius and texture
void DisplaySphere (double R, GLuint texture){
This variable will control which vertex we are currently working with
int b;
I have chosen to scale it here to 0.0125 times its original size, and then
increase it by R as the original sphere is rather large.
glScalef (0.0125 * R, 0.0125 * R, 0.0125 * R);
Now I am rotating it because, if you run it, it is sideways 🙂
glRotatef (90, 1, 0, 0);
Now I bind the texure we inputted above.
glBindTexture (GL_TEXTURE_2D, texture);
Now to begin drawing the sphere itself. I am drawing it with triangle strips
as they are the fastest shape for this.
glBegin (GL_TRIANGLE_STRIP);
Now I am looping through each vertex
for ( b = 0; b <= VertexCount; b++){
Assigning the texture coordinates of the current vertex
glTexCoord2f (VERTEX[b].U, VERTEX[b].V);
And the drawing the specified vertex with the Z coordinate inverted. Because
our creation code only draws half a sphere, which is why I am also doing
this loop again for the other half below.
glVertex3f (VERTEX[b].X, VERTEX[b].Y, -VERTEX[b].Z);
}
And here I do the same as above, only this time, I invert
the V(y) texture coordinate.
for ( b = 0; b <= VertexCount; b++){
glTexCoord2f (VERTEX[b].U, -VERTEX[b].V);
glVertex3f (VERTEX[b].X, VERTEX[b].Y, VERTEX[b].Z);
}
Then end the shape.
glEnd();
}
———————————————————————————
And there we have it. A textured sphere without the need for GLUT
If you have any questions, please email me at swiftless@gmail.com
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. |
#include <GL/gl.h> #include <GL/glut.h> #include <windows.h> #include <stdio.h> #include <math.h> #include <iostream.h> GLuint texture[1]; double angle = 0; typedef struct const double PI = 3.1415926535897; const space = 10; const VertexCount = (90 / space) * (360 / space) * 4; VERTICES VERTEX[VertexCount]; GLuint LoadTextureRAW( const char * filename ); void DisplaySphere (double R, GLuint texture){ void CreateSphere (double R, double H, double K, double Z) { for( a = 0; a <= 360 – space; a+=space){ 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].V = (2 * b) / 360; VERTEX[n].U = (a) / 360; n++; 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].V = (2 * (b + space)) / 360; VERTEX[n].U = (a) / 360; n++; 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].V = (2 * b) / 360; VERTEX[n].U = (a + space) / 360; n++; 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].V = (2 * (b + space)) / 360; VERTEX[n].U = (a + space) / 360; n++; } } } void display (void) { void init (void) { int main (int argc, char **argv) { GLuint LoadTextureRAW( const char * filename ) |
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