I am not able to create a simple 3D sphere using the OpenGL library function glutSolidSphere() in C++.
Here's what I tried:
#include<GL/glu.h>
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0.0,0.0);
glLoadIdentity();
glutSolidSphere( 5.0, 20.0, 20.0);
glFlush();
}
void myInit()
{
glClearColor(1.0,1.0,1.0,1.0);
glColor3f(1.0,0.0,0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,499.0,0.0,499.0);
glMatrixMode(GL_MODELVIEW);
}
void main(int argc,char **argv)
{
qobj = gluNewQuadric();
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutCreateWindow("pendulum");
glutDisplayFunc(display);
myInit();
glutMainLoop();
}
In OpenGL you don't create objects, you just draw them. Once they are drawn, OpenGL no longer cares about what geometry you sent it.
glutSolidSphere is just sending drawing commands to OpenGL. However there's nothing special in and about it. And since it's tied to GLUT I'd not use it. Instead, if you really need some sphere in your code, how about create if for yourself?
#define _USE_MATH_DEFINES
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include <cmath>
// your framework of choice here
class SolidSphere
{
protected:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
public:
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;
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 * 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;
}
}
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, &vertices[0]);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
glPopMatrix();
}
};
SolidSphere sphere(1, 12, 24);
void display()
{
int const win_width = …; // retrieve window dimensions from
int const win_height = …; // framework of choice here
float const win_aspect = (float)win_width / (float)win_height;
glViewport(0, 0, win_width, win_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#ifdef DRAW_WIREFRAME
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
sphere.draw(0, 0, -5);
swapBuffers();
}
int main(int argc, char *argv[])
{
// initialize and register your framework of choice here
return 0;
}
It doesn't seem like anyone so far has addressed the actual problem with your original code, so I thought I would do that even though the question is quite old at this point.
The problem originally had to do with the projection in relation to the radius and position of the sphere. I think you'll find that the problem isn't too complicated. The program actually works correctly, it's just that what is being drawn is very hard to see.
First, an orthogonal projection was created using the call
gluOrtho2D(0.0, 499.0, 0.0, 499.0);
which "is equivalent to calling glOrtho with near = -1 and far = 1." This means that the viewing frustum has a depth of 2. So a sphere with a radius of anything greater than 1 (diameter = 2) will not fit entirely within the viewing frustum.
Then the calls
glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
are used, which loads the identity matrix of the model-view matrix and then "[r]enders a sphere centered at the modeling coordinates origin of the specified radius." Meaning, the sphere is rendered at the origin, (x, y, z) = (0, 0, 0), and with a radius of 5.
Now, the issue is three-fold:
Since the window is 500x500 pixels and the width and height of the viewing frustum is almost 500 (499.0), the small radius of the sphere (5.0) makes its projected area only slightly over one fiftieth (2*5/499) of the size of the window in each dimension. This means that the apparent size of the sphere would be roughly 1/2,500th (actually pi*5^2/499^2, which is closer to about 1/3170th) of the entire window, so it might be difficult to see. This is assuming the entire circle is drawn within the area of the window. It is not, however, as we will see in point 2.
Since the viewing frustum has it's left plane at x = 0 and bottom plane at y = 0, the sphere will be rendered with its geometric center in the very bottom left corner of the window so that only one quadrant of the projected sphere will be visible! This means that what would be seen is even smaller, about 1/10,000th (actually pi*5^2/(4*499^2), which is closer to 1/12,682nd) of the window size. This would make it even more difficult to see. Especially since the sphere is rendered so close to the edges/corner of the screen where you might not think to look.
Since the depth of the viewing frustum is significantly smaller than the diameter of the sphere (less than half), only a sliver of the sphere will be within the viewing frustum, rendering only that part. So you will get more like a hollow circle on the screen than a solid sphere/circle. As it happens, the thickness of that sliver might represent less than 1 pixel on the screen which means we might even see nothing on the screen, even if part of the sphere is indeed within the viewing frustum.
The solution is simply to change the viewing frustum and radius of the sphere. For instance,
gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);
renders the following image.
As you can see, only a small part is visible around the "equator", of the sphere with a radius of 5. (I changed the projection to fill the window with the sphere.) Another example,
gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);
renders the following image.
The image above shows more of the sphere inside of the viewing frustum, but still the sphere is 0.2 depth units larger than the viewing frustum. As you can see, the "ice caps" of the sphere are missing, both the north and the south. So, if we want the entire sphere to fit within the viewing frustum which has depth 2, we must make the radius less than or equal to 1.
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);
renders the following image.
I hope this has helped someone. Take care!
I don't understand how can datenwolf`s index generation can be correct. But still I find his solution rather clear. This is what I get after some thinking:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + (s+1));
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int 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 );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
push_indices(indices, sectors, r, s);
}
}
}
Here's the code:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
I like the answer of coin. It's simple to understand and works with triangles. However the indexes of his program are sometimes over the bounds. So I post here his code with two tiny corrections:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
int nextS = (s+1) % sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + nextS);
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int 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 );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
if(r < rings-1)
push_indices(indices, sectors, r, s);
}
}
}
Datanewolf's code is ALMOST right. I had to reverse both the winding and the normals to make it work properly with the fixed pipeline. The below works correctly with cull on or off for me:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
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 * 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++) {
/*
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
*/
*i++ = (r+1) * sectors + s;
*i++ = (r+1) * sectors + (s+1);
*i++ = r * sectors + (s+1);
*i++ = r * sectors + s;
}
Edit: There was a question on how to draw this... in my code I encapsulate these values in a G3DModel class. This is my code to setup the frame, draw the model, and end it:
void GraphicsProvider3DPriv::BeginFrame()const{
int win_width;
int win_height;// framework of choice here
glfwGetWindowSize(window, &win_width, &win_height); // retrieve window
float const win_aspect = (float)win_width / (float)win_height;
// set lighting
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
GLfloat lightpos[] = {0, 0.0, 0, 0.};
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
// set up world transform
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT|GL_ACCUM_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
}
void GraphicsProvider3DPriv::DrawModel(const G3DModel* model, const Transform3D transform)const{
G3DModelPriv* privModel = (G3DModelPriv *)model;
glPushMatrix();
glLoadMatrixf(transform.GetOGLData());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &privModel->vertices[0]);
glNormalPointer(GL_FLOAT, 0, &privModel->normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &privModel->texcoords[0]);
glEnable(GL_TEXTURE_2D);
//glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, privModel->texname);
glDrawElements(GL_QUADS, privModel->indices.size(), GL_UNSIGNED_SHORT, &privModel->indices[0]);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
void GraphicsProvider3DPriv::EndFrame()const{
/* Swap front and back buffers */
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glDisable(GL_CULL_FACE);
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
Related
I am not able to create a simple 3D sphere using the OpenGL library function glutSolidSphere() in C++.
Here's what I tried:
#include<GL/glu.h>
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0.0,0.0);
glLoadIdentity();
glutSolidSphere( 5.0, 20.0, 20.0);
glFlush();
}
void myInit()
{
glClearColor(1.0,1.0,1.0,1.0);
glColor3f(1.0,0.0,0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,499.0,0.0,499.0);
glMatrixMode(GL_MODELVIEW);
}
void main(int argc,char **argv)
{
qobj = gluNewQuadric();
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutCreateWindow("pendulum");
glutDisplayFunc(display);
myInit();
glutMainLoop();
}
In OpenGL you don't create objects, you just draw them. Once they are drawn, OpenGL no longer cares about what geometry you sent it.
glutSolidSphere is just sending drawing commands to OpenGL. However there's nothing special in and about it. And since it's tied to GLUT I'd not use it. Instead, if you really need some sphere in your code, how about create if for yourself?
#define _USE_MATH_DEFINES
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include <cmath>
// your framework of choice here
class SolidSphere
{
protected:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
public:
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;
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 * 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;
}
}
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, &vertices[0]);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
glPopMatrix();
}
};
SolidSphere sphere(1, 12, 24);
void display()
{
int const win_width = …; // retrieve window dimensions from
int const win_height = …; // framework of choice here
float const win_aspect = (float)win_width / (float)win_height;
glViewport(0, 0, win_width, win_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#ifdef DRAW_WIREFRAME
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
sphere.draw(0, 0, -5);
swapBuffers();
}
int main(int argc, char *argv[])
{
// initialize and register your framework of choice here
return 0;
}
It doesn't seem like anyone so far has addressed the actual problem with your original code, so I thought I would do that even though the question is quite old at this point.
The problem originally had to do with the projection in relation to the radius and position of the sphere. I think you'll find that the problem isn't too complicated. The program actually works correctly, it's just that what is being drawn is very hard to see.
First, an orthogonal projection was created using the call
gluOrtho2D(0.0, 499.0, 0.0, 499.0);
which "is equivalent to calling glOrtho with near = -1 and far = 1." This means that the viewing frustum has a depth of 2. So a sphere with a radius of anything greater than 1 (diameter = 2) will not fit entirely within the viewing frustum.
Then the calls
glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
are used, which loads the identity matrix of the model-view matrix and then "[r]enders a sphere centered at the modeling coordinates origin of the specified radius." Meaning, the sphere is rendered at the origin, (x, y, z) = (0, 0, 0), and with a radius of 5.
Now, the issue is three-fold:
Since the window is 500x500 pixels and the width and height of the viewing frustum is almost 500 (499.0), the small radius of the sphere (5.0) makes its projected area only slightly over one fiftieth (2*5/499) of the size of the window in each dimension. This means that the apparent size of the sphere would be roughly 1/2,500th (actually pi*5^2/499^2, which is closer to about 1/3170th) of the entire window, so it might be difficult to see. This is assuming the entire circle is drawn within the area of the window. It is not, however, as we will see in point 2.
Since the viewing frustum has it's left plane at x = 0 and bottom plane at y = 0, the sphere will be rendered with its geometric center in the very bottom left corner of the window so that only one quadrant of the projected sphere will be visible! This means that what would be seen is even smaller, about 1/10,000th (actually pi*5^2/(4*499^2), which is closer to 1/12,682nd) of the window size. This would make it even more difficult to see. Especially since the sphere is rendered so close to the edges/corner of the screen where you might not think to look.
Since the depth of the viewing frustum is significantly smaller than the diameter of the sphere (less than half), only a sliver of the sphere will be within the viewing frustum, rendering only that part. So you will get more like a hollow circle on the screen than a solid sphere/circle. As it happens, the thickness of that sliver might represent less than 1 pixel on the screen which means we might even see nothing on the screen, even if part of the sphere is indeed within the viewing frustum.
The solution is simply to change the viewing frustum and radius of the sphere. For instance,
gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);
renders the following image.
As you can see, only a small part is visible around the "equator", of the sphere with a radius of 5. (I changed the projection to fill the window with the sphere.) Another example,
gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);
renders the following image.
The image above shows more of the sphere inside of the viewing frustum, but still the sphere is 0.2 depth units larger than the viewing frustum. As you can see, the "ice caps" of the sphere are missing, both the north and the south. So, if we want the entire sphere to fit within the viewing frustum which has depth 2, we must make the radius less than or equal to 1.
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);
renders the following image.
I hope this has helped someone. Take care!
I don't understand how can datenwolf`s index generation can be correct. But still I find his solution rather clear. This is what I get after some thinking:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + (s+1));
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int 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 );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
push_indices(indices, sectors, r, s);
}
}
}
Here's the code:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
I like the answer of coin. It's simple to understand and works with triangles. However the indexes of his program are sometimes over the bounds. So I post here his code with two tiny corrections:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
int nextS = (s+1) % sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + nextS);
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int 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 );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
if(r < rings-1)
push_indices(indices, sectors, r, s);
}
}
}
Datanewolf's code is ALMOST right. I had to reverse both the winding and the normals to make it work properly with the fixed pipeline. The below works correctly with cull on or off for me:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
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 * 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++) {
/*
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
*/
*i++ = (r+1) * sectors + s;
*i++ = (r+1) * sectors + (s+1);
*i++ = r * sectors + (s+1);
*i++ = r * sectors + s;
}
Edit: There was a question on how to draw this... in my code I encapsulate these values in a G3DModel class. This is my code to setup the frame, draw the model, and end it:
void GraphicsProvider3DPriv::BeginFrame()const{
int win_width;
int win_height;// framework of choice here
glfwGetWindowSize(window, &win_width, &win_height); // retrieve window
float const win_aspect = (float)win_width / (float)win_height;
// set lighting
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
GLfloat lightpos[] = {0, 0.0, 0, 0.};
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
// set up world transform
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT|GL_ACCUM_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
}
void GraphicsProvider3DPriv::DrawModel(const G3DModel* model, const Transform3D transform)const{
G3DModelPriv* privModel = (G3DModelPriv *)model;
glPushMatrix();
glLoadMatrixf(transform.GetOGLData());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &privModel->vertices[0]);
glNormalPointer(GL_FLOAT, 0, &privModel->normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &privModel->texcoords[0]);
glEnable(GL_TEXTURE_2D);
//glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, privModel->texname);
glDrawElements(GL_QUADS, privModel->indices.size(), GL_UNSIGNED_SHORT, &privModel->indices[0]);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
void GraphicsProvider3DPriv::EndFrame()const{
/* Swap front and back buffers */
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glDisable(GL_CULL_FACE);
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
I'm writing a program to graph 3D surfaces. It uses CUDA to do rendering and then OpenGL to display the results. It works fine and renders pretty and accurate results, but it does not correctly update the window. Here's my program in pseudocode:
void display() {
//render stuff
glutSwapBuffers();
glutPostRedisplay();
}
int main() {
//set up OpenGL and CUDA
glutMainLoop();
return 0;
}
This should update continuously, but it doesn't. It correctly calls the display method and CUDA renders the results, but it does not display the results in the window until the window is resized. So it calls the display function something like 30 times a second, but the window doesn't actually show the results until resized or minimized.
Here's my entire program. It's EXTREMELY messy and has no comments since I was learning OpenGL as I went along.
#define GL_GLEXT_PROTOTYPES
#include <GL/freeglut_std.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <cuda_gl_interop.h>
#define XSIZE 1280
#define YSIZE 640
float theta = .15;
float phi = 1;
float r = 10;
float3 lightDirection = make_float3(9, 5, -5);
float delta = .001;
GLuint pbo; // OpenGL pixel buffer object
struct cudaGraphicsResource *cuda_pbo_resource; // handles OpenGL-CUDA exchange
GLuint texid; // Texture
GLuint shader;
__host__ __device__ float3 operator+(float3 a, float3 b) {
return make_float3(a.x + b.x, a.y + b.y, a.z + b.z);
}
__host__ __device__ float3 operator-(float3 a, float3 b) {
return make_float3(a.x - b.x, a.y - b.y, a.z - b.z);
}
__host__ __device__ float3 operator*(float3 a, float b) {
return make_float3(a.x * b, a.y * b, a.z * b);
}
__host__ __device__ float3 operator/(float3 a, float b) {
return make_float3(a.x / b, a.y / b, a.z / b);
}
__host__ __device__ float operator*(float3 a, float3 b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
__host__ __device__ float magnitude(float3 a) {
return std::sqrt(a * a);
}
__host__ __device__ float3 normalize(float3 a) {
return a / magnitude(a);
}
__device__ float valueAt(float3 a) {
return std::sin(a.x) + std::sin(a.y) + std::sin(a.z);
}
__device__ float3 normalAt(float3 a) {
float x = valueAt(make_float3(a.x + .001, a.y, a.z)) - valueAt(a);
float y = valueAt(make_float3(a.x, a.y + .001, a.z)) - valueAt(a);
float z = valueAt(make_float3(a.x, a.y, a.z + .001)) - valueAt(a);
return make_float3(x, y, z) / .001;
}
__device__ float estimateDistance(float3 a) {
return std::abs(valueAt(a) / magnitude(normalAt(a)));
}
__device__ float3 trace(float3 from, float3 direction) {
float totalDistance = 0;
float3 v = from;
for(int steps = 0; steps < 256; steps++) {
if(magnitude(v) < 5) break;
totalDistance += 1;
v = from + direction * totalDistance;
}
for(int steps = 0; steps < 256; steps++) {
v = from + direction * totalDistance;
float distance = estimateDistance(v);
totalDistance += distance * .1;
if(distance < .1 && magnitude(v) < 5) return v;
}
return make_float3(0, 0, 0);
}
__device__ uchar4 colorAt(float3 v, float3 lightDirection, float3 viewDirection) {
float3 normal = normalize(normalAt(v));
float3 h = normalize(viewDirection + lightDirection);
float specular = std::abs(std::pow(h * normal, 15)) * .5;
float diffuse = std::abs(lightDirection * normal) * .5;
float lighting = (specular + diffuse) * .9;
lighting += .1;
float3 color;
if((fmod(v.x + 1000, 1) < .5 && fmod(v.y + 1000, 1) < .5) || (fmod(v.x + 1000, 1) > .5 && fmod(v.y + 1000, 1) > .5)) {
color.x = 1;
color.y = .5;
color.z = .25;
}
else {
color.x = .75;
color.y = .5;
color.z = .25;
}
color = color * lighting;
return make_uchar4((unsigned char)(color.x * 255), (unsigned char)(color.y * 255), (unsigned char)(color.z * 255), 255);
}
__global__ void eval(float3 position, float3 direction, float3 right, float3 up, float3 lightDirection, float delta, uchar4* dev_pixels) {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int offset = x + y * XSIZE;
float rAngle = (x - XSIZE / 2) * delta;
float uAngle = (-y + YSIZE / 2) * delta;
float3 rayDirection = direction + right * rAngle + up * uAngle;
rayDirection = normalize(rayDirection);
float3 v = trace(position, rayDirection);
uchar4 color;
if(magnitude(v) != 0) color = colorAt(v, lightDirection, direction);
dev_pixels[offset] = color;
}
void display() {
uchar4* dev_pixels;
cudaGLRegisterBufferObject(pbo);
cudaGLMapBufferObject((void**)&dev_pixels, pbo);
dim3 dim_block(16, 16);
dim3 dim_grid(XSIZE / 16, YSIZE / 16);
float3 position = make_float3(std::cos(theta) * std::sin(phi) * r, std::sin(theta) * std::sin(phi) * r, std::cos(phi) * r);
float3 direction = normalize(position) * -1;
float3 right = make_float3(-std::sin(theta), std::cos(theta), 0);
right = normalize(right);
float3 up = make_float3(-std::cos(phi) * std::cos(theta), -std::cos(phi) * std::sin(theta), std::sin(phi));
up = normalize(up);
eval<<<dim_grid, dim_block>>>(position, direction, right, up, lightDirection, delta, dev_pixels);
cudaGLUnmapBufferObject(pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBindTexture(GL_TEXTURE_2D, texid);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XSIZE, YSIZE, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,1.0f); glVertex3f(0.0f,0.0f,0.0f);
glTexCoord2f(0.0f,0.0f); glVertex3f(0.0f,1.0f,0.0f);
glTexCoord2f(1.0f,0.0f); glVertex3f(1.0f,1.0f,0.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f(1.0f,0.0f,0.0f);
glEnd();
glutSwapBuffers();
theta += .01;
glutPostRedisplay();
}
int main(int argc, char **argv) {
lightDirection = normalize(lightDirection);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(XSIZE, YSIZE);
glutCreateWindow("Grapher");
glutDisplayFunc(display);
glViewport(0, 0, XSIZE, YSIZE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1.0f, 0, 1.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.5f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, XSIZE * YSIZE * 4, NULL, GL_DYNAMIC_COPY);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, XSIZE, YSIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glutMainLoop();
return 0;
}
I'm using NSight Eclipse on Ubuntu 12.04.
At the end of your display routine, add these 2 lines:
...
glTexCoord2f(1.0f,1.0f); glVertex3f(1.0f,0.0f,0.0f);
glEnd();
glutSwapBuffers();
glClear(GL_DEPTH_BUFFER_BIT); // add this line
cudaGLUnregisterBufferObject(pbo); // add this line
theta += .01;
glutPostRedisplay();
...
The first addition allows the animation to proceed frame by frame. (Frames after the first will render properly.)
The second addition makes it so that if you close the animation window, you won't get a seg fault. cudaGLUnregisterBufferObject must be called on any previously registered object before the underlying OpenGL resource gets freed (or else you get a seg fault). When you click the X to close the window, the OpenGL buffer object (pbo) gets freed as the OpenGL context disappears.
I want to calculate all the vertices needed and connect them with lines, so I eventually come up with a sphere. How many ways are there to do it? And also the lines between the vertices, will be straight; how can I make them "curved" I know that I can use glutWireSphere(), but I am interested in actually calculating the vertices. A way that I thought about it, was to put all the vertices manually in an array, but I guess that is not the way it's done.
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.
There's more than one way to do this: a) icosphere generation and b)UV sphere generation. There may be more methods to do this. Some googling got me this excellent post on icosphere generation. I couldn't find UV sphere generation method though.
Paul Bourke actually has a nice introduction to sphere generation. And as for curved lines, there is no such thing in OpenGL. You can only make them appear curved by adding more intermediate connected points.
datenwolf's answer is great but contains some error.
When you use vbo, client states must be disabled after enabled.
Add Three lines to draw code
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);
**glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);**
glPopMatrix();
}
An iconosphere would do the trick .
Still , to make a sphere with it , you will have to subdivide it's triangles.
It seems to me there's a great abundance of tutorials on how to make icospheres, but not so much about the method of facet approximation using polar coordinates.
So here's a very slightly edited code sample from the OpenGL Superbible 4th Edition book by Richard S. Wright Jr.
Since it's a very bare bones usage of the fixed-function pipeline (no glDrawElements, etc...) I found it useful for educational purposes.
Stacks are drawn as series of triangle strips. Obviously not the optimal performance, but it works!
// For best results, put this in a display list
// Draw a sphere at the origin
void RenderSphere(const float fRadius, const int iStacks, const int iSlices)
{
const auto PI = (float)M_PI;
const auto PIx2 = (float)(M_PI * 2.0);
GLfloat drho = PI / (GLfloat)iStacks;
GLfloat dtheta = PIx2 / (GLfloat)iSlices;
GLfloat ds = 1.0f / (GLfloat)iSlices;
GLfloat dt = 1.0f / (GLfloat)iStacks;
GLfloat t = 1.0f;
GLfloat s = 0.0f;
for (int i = 0; i < iStacks; i++)
{
const GLfloat rho = (GLfloat)i * drho;
const GLfloat srho = (GLfloat)(std::sinf(rho));
const GLfloat crho = (GLfloat)(std::cosf(rho));
const GLfloat srhodrho = (GLfloat)(std::sinf(rho + drho));
const GLfloat crhodrho = (GLfloat)(std::cosf(rho + drho));
// Many sources of OpenGL sphere drawing code uses a triangle fan
// for the caps of the sphere. This however introduces texturing
// artifacts at the poles on some OpenGL implementations
glBegin(GL_TRIANGLE_STRIP);
s = 0.0f;
for (int j = 0; j <= iSlices; j++)
{
const GLfloat theta = (j == iSlices) ? 0.0f : j * dtheta;
const GLfloat stheta = (GLfloat)(-std::sinf(theta));
const GLfloat ctheta = (GLfloat)(std::cosf(theta));
GLfloat x = stheta * srho;
GLfloat y = ctheta * srho;
GLfloat z = crho;
glTexCoord2f(s, t);
glNormal3f(x, y, z);
glVertex3f(x * fRadius, y * fRadius, z * fRadius);
x = stheta * srhodrho;
y = ctheta * srhodrho;
z = crhodrho;
glTexCoord2f(s, t - dt);
s += ds;
glNormal3f(x, y, z);
glVertex3f(x * fRadius, y * fRadius, z * fRadius);
}
glEnd();
t -= dt;
}
}
Unfortunately I couldn't find back a link to the original online repository of this source code, it's pretty ancient. Feel free to post if you know where to find it !
I want to calculate all the vertices needed and connect them with lines, so I eventually come up with a sphere. How many ways are there to do it? And also the lines between the vertices, will be straight; how can I make them "curved" I know that I can use glutWireSphere(), but I am interested in actually calculating the vertices. A way that I thought about it, was to put all the vertices manually in an array, but I guess that is not the way it's done.
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.
There's more than one way to do this: a) icosphere generation and b)UV sphere generation. There may be more methods to do this. Some googling got me this excellent post on icosphere generation. I couldn't find UV sphere generation method though.
Paul Bourke actually has a nice introduction to sphere generation. And as for curved lines, there is no such thing in OpenGL. You can only make them appear curved by adding more intermediate connected points.
datenwolf's answer is great but contains some error.
When you use vbo, client states must be disabled after enabled.
Add Three lines to draw code
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);
**glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);**
glPopMatrix();
}
An iconosphere would do the trick .
Still , to make a sphere with it , you will have to subdivide it's triangles.
It seems to me there's a great abundance of tutorials on how to make icospheres, but not so much about the method of facet approximation using polar coordinates.
So here's a very slightly edited code sample from the OpenGL Superbible 4th Edition book by Richard S. Wright Jr.
Since it's a very bare bones usage of the fixed-function pipeline (no glDrawElements, etc...) I found it useful for educational purposes.
Stacks are drawn as series of triangle strips. Obviously not the optimal performance, but it works!
// For best results, put this in a display list
// Draw a sphere at the origin
void RenderSphere(const float fRadius, const int iStacks, const int iSlices)
{
const auto PI = (float)M_PI;
const auto PIx2 = (float)(M_PI * 2.0);
GLfloat drho = PI / (GLfloat)iStacks;
GLfloat dtheta = PIx2 / (GLfloat)iSlices;
GLfloat ds = 1.0f / (GLfloat)iSlices;
GLfloat dt = 1.0f / (GLfloat)iStacks;
GLfloat t = 1.0f;
GLfloat s = 0.0f;
for (int i = 0; i < iStacks; i++)
{
const GLfloat rho = (GLfloat)i * drho;
const GLfloat srho = (GLfloat)(std::sinf(rho));
const GLfloat crho = (GLfloat)(std::cosf(rho));
const GLfloat srhodrho = (GLfloat)(std::sinf(rho + drho));
const GLfloat crhodrho = (GLfloat)(std::cosf(rho + drho));
// Many sources of OpenGL sphere drawing code uses a triangle fan
// for the caps of the sphere. This however introduces texturing
// artifacts at the poles on some OpenGL implementations
glBegin(GL_TRIANGLE_STRIP);
s = 0.0f;
for (int j = 0; j <= iSlices; j++)
{
const GLfloat theta = (j == iSlices) ? 0.0f : j * dtheta;
const GLfloat stheta = (GLfloat)(-std::sinf(theta));
const GLfloat ctheta = (GLfloat)(std::cosf(theta));
GLfloat x = stheta * srho;
GLfloat y = ctheta * srho;
GLfloat z = crho;
glTexCoord2f(s, t);
glNormal3f(x, y, z);
glVertex3f(x * fRadius, y * fRadius, z * fRadius);
x = stheta * srhodrho;
y = ctheta * srhodrho;
z = crhodrho;
glTexCoord2f(s, t - dt);
s += ds;
glNormal3f(x, y, z);
glVertex3f(x * fRadius, y * fRadius, z * fRadius);
}
glEnd();
t -= dt;
}
}
Unfortunately I couldn't find back a link to the original online repository of this source code, it's pretty ancient. Feel free to post if you know where to find it !
i am making a program to animate a ball over a field
it goes like,
void display()
{
/*
code to draw a field
*/
loop:1 to 5
loop:1 to 6
/*
here comes the code to animate the ball over the field
*/
}
I want whenever user press "right_Arrow" the whole scene get rotated by
some angle
problem:whenever the user presses "right_Arrow" key it rotate the scene by some angle but also display whole animation again...but i want animation to be displayed only once(after maximazing window) and then the user can rotate the whole scene
by some angle without displaying any animation on pressing "right_Arrow" key
How should i do this in my code?
Again your problem lies in your mistake to do the animation completely in the display function. I extended my example I wrote you yesterday a bit; After starting the program, the animation will play for 5 seconds, then stop. Pressing [R] resets the animation (thus starts it again), pressing [+] / [-] rotates the scene around the Y axis.
http://homepages.physik.uni-muenchen.de/~Wolfgang.Draxinger/stuff/sinsphere_rot.c
EDIT: commenting
/* This is ANSI-C - don't try to compile with a C++ compiler, it will fail! */
#include <GL/glut.h>
#include <stdlib.h>
#include <sys/time.h>
#include <math.h>
#define M_PI 3.1415926535897932384626433832795029L
#define M_PI_2 1.5707963267948966192313216916397514L
# define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
void idle(void);
void animate(float dT);
void display(void);
void keyboard(unsigned char key, int x, int y);
void init_sphere(unsigned int rings, unsigned int sectors);
void draw_sphere(void);
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("SinSphere");
glutIdleFunc(idle);
glutKeyboardFunc(keyboard);
glutDisplayFunc(display);
init_sphere(10, 30);
glutMainLoop();
return 0;
}
This is all just boilerplate code, nothing special.
struct AnimationState
{
float time;
float duration;
float sphere_speed;
float sphere_path_radius;
float sphere_path_bobbing;
float sphere_position[3];
};
static struct AnimationState animation = {
0.,
5., /* play for 5 seconds */
0.1, 3., 1.,
{1., 0., 0.}
};
AnimationState got an additional element duration; after that time the animation will stop playing, by testing if animation.time < animation.duration and only advance the animation step if so.
void animate(float dT)
{
if(animation.time < animation.duration) {
animation.time += dT;
animation.sphere_position[0] = animation.sphere_path_radius * cos(2*M_PI * animation.time * animation.sphere_speed);
animation.sphere_position[1] = animation.sphere_path_bobbing * sin(2*M_PI * animation.time * 5 * animation.sphere_speed);
animation.sphere_position[2] = animation.sphere_path_radius * sin(2*M_PI * animation.time * animation.sphere_speed);
}
}
struct ViewState {
float rotation;
float rotation_step;
};
static struct ViewState view = {
0.,
0.1
};
ViewState stores the rotation. This is really a dumbed down version, usually implements this through a view transformation matix and steppings for that.
void keyboard(unsigned char key, int x, int y)
{
switch(key) {
case 'R':
case 'r': /* restart animation */
animation.time = 0.;
break;
case '+':
view.rotation += view.rotation_step;
break;
case '-':
view.rotation -= view.rotation_step;
break;
}
glutPostRedisplay();
}
Keyboard handler function should be obvious.
GLfloat *sphere_vertices_normals;
unsigned int sphere_quads = 0;
GLushort *sphere_indices;
void init_sphere(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_normals = malloc(sizeof(GLfloat)*3 * rings*sectors);
GLfloat *v = sphere_vertices_normals;
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 );
v[0] = x;
v[1] = y;
v[2] = z;
v+=3;
}
sphere_indices = malloc(sizeof(GLushort) * rings * sectors * 4);
GLushort *i = sphere_indices;
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;
sphere_quads++;
}
}
init_sphere build us some nice vertex and index array containing sphere geometry. Exercise for the reader: Put it in a Vertex Buffer Object.
void draw_sphere()
{
glTranslatef(animation.sphere_position[0], animation.sphere_position[1], animation.sphere_position[2]);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, sphere_vertices_normals);
glNormalPointer(GL_FLOAT, 0, sphere_vertices_normals);
glDrawElements(GL_QUADS, sphere_quads*4, GL_UNSIGNED_SHORT, sphere_indices);
}
void idle()
{
glutPostRedisplay();
}
The idle function gets called after all input events have been processed. Input events are keypresses and the like. GLUT events only get processed after the display handler returns. So you must not implement the animation timer loop within the display handler. Instead you determine the time for a single display, then advance the loop by that timestep for the next display iteration. idle initiate the next display pass after event processing.
static GLfloat const light_pos[4] = {-1., 1., 1., 0.};
static GLfloat const light_color[4] = {1., 1., 1., 1.};
void display()
{
static struct timeval delta_T = {0., 0.};
struct timeval time_frame_begin, time_frame_end;
int win_width, win_height;
float win_aspect;
gettimeofday(&time_frame_begin, 0);
animate(delta_T.tv_sec + delta_T.tv_usec * 1.e-6);
win_width = glutGet(GLUT_WINDOW_WIDTH);
win_height = glutGet(GLUT_WINDOW_HEIGHT);
win_aspect = (float)win_width/(float)win_height;
glViewport(0, 0, win_width, win_height);
glClearColor(0.6, 0.6, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-win_aspect, win_aspect, -1., 1., 1., 10.);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0,0,-5.5);
glRotatef(view.rotation * 180./M_PI, 0, 1, 0);
glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
glPushMatrix();
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
draw_sphere();
glPopMatrix();
glutSwapBuffers();
gettimeofday(&time_frame_end, 0);
timersub(&time_frame_end, &time_frame_begin, &delta_T);
}
Instead of measureing the time spent in display a more accurate approach is measuring the time between each invocation of display to take time spent in other parts of the programm into account. This is an exercise left to the reader.