方法:使用多边形表面近似表示三维物体的subdivision法
绘制圆柱核心代码如下:
void drawsurface(float radius, float height, int nslice, int nstack)
// nslice --- Number of subdivision around z-axis
// nstack --- Number of subdivision along z-axis
{
float a=2*PI/nslice;
float h=-height/2,dh=height/nstack;
for(int i=0;i<=nslice;i++)
{
glBegin(GL_QUAD_STRIP);
for(int j=-nstack/2;j<=nstack/2;j++)
{
glNormal3f(radius*cos(a*i),radius*sin(a*i),0);
glVertex3f(radius*cos(a*i),radius*sin(a*i),dh*j);
glNormal3f(radius*cos(a*(i+1)),radius*sin(a*(i+1)),0);
glVertex3f(radius*cos(a*(i+1)),radius*sin(a*(i+1)),dh*j);
}
glEnd();
}
}
void drawDisk(float radius, int nslice, int nring)
//半径 圆周向细分数 半径向细分数
// nslice --- Number of subdivision around z-axis
// nring --- Number of concentric rings on each end face
{
glNormal3f(0,0,1);
float a=2*PI/nslice;//单位角度
float r=radius/nring,nr;//单位半径长度,当前半径长度
for(int i=0;i<nslice;i++)
{
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0,0,0);
glVertex3f(r*cos(a*i),r*sin(a*i),0);
glVertex3f(r*cos(a*(i+1)),r*sin(a*(i+1)),0);
glEnd();
glBegin(GL_QUAD_STRIP);
nr=0;
for(int j=0;j<nring;j++)
{
nr+=r;
glVertex3f(nr*cos(a*(i+1)),nr*sin(a*(i+1)),0);
glVertex3f(nr*cos(a*i),nr*sin(a*i),0);
}
glEnd();
}
}
完整代码如下:
// ====== Computer Graphics Experiment #9 ======
// | Cylinder rendering |
// =============================================
//
// Requirement:
// (1) Implement cylinder rendering function.
// (2) Change polygon drawing mode and face culling parameters
// and observe the effects.
// (3) Change smooth shading to flat shading and observe the effects
// (4) Carefully read and understand the rest of the source code
#include
#include
#include
#define PI 3.14159265
float xrotate, yrotate, zrotate;
void drawsurface(float radius, float height, int nslice, int nstack)
// nslice --- Number of subdivision around z-axis
// nstack --- Number of subdivision along z-axis
{
float a=2*PI/nslice;
float h=-height/2,dh=height/nstack;
for(int i=0;i<=nslice;i++)
{
glBegin(GL_QUAD_STRIP);
for(int j=-nstack/2;j<=nstack/2;j++)
{
glNormal3f(radius*cos(a*i),radius*sin(a*i),0);
glVertex3f(radius*cos(a*i),radius*sin(a*i),dh*j);
glNormal3f(radius*cos(a*(i+1)),radius*sin(a*(i+1)),0);
glVertex3f(radius*cos(a*(i+1)),radius*sin(a*(i+1)),dh*j);
}
glEnd();
}
}
void drawDisk(float radius, int nslice, int nring)
//半径 圆周向细分数 半径向细分数
// nslice --- Number of subdivision around z-axis
// nring --- Number of concentric rings on each end face
{
glNormal3f(0,0,1);
float a=2*PI/nslice;//单位角度
float r=radius/nring,nr;//单位半径长度,当前半径长度
for(int i=0;i<nslice;i++)
{
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0,0,0);
glVertex3f(r*cos(a*i),r*sin(a*i),0);
glVertex3f(r*cos(a*(i+1)),r*sin(a*(i+1)),0);
glEnd();
glBegin(GL_QUAD_STRIP);
nr=0;
for(int j=0;j<nring;j++)
{
nr+=r;
glVertex3f(nr*cos(a*(i+1)),nr*sin(a*(i+1)),0);
glVertex3f(nr*cos(a*i),nr*sin(a*i),0);
}
glEnd();
}
}
// render a cylinder centered at the origin, with z as axis.
void MyCylinder(float radius, float height,
int nslice, int nstack, int nring)
// nslice --- Number of subdivision around z-axis
// nstack --- Number of subdivision along z-axis
// nring --- Number of concentric rings on each end face
{
drawsurface(radius,height,nslice,nstack);
glTranslatef(0,0,height/2);
drawDisk(radius,nslice,nring);
glTranslatef(0,0,-height);
glRotatef(180,1,0,0);
drawDisk(radius,nslice,nring);
}
class CVector3D
{
public:
float x, y, z;
// Constructors
CVector3D(void) {x=0.0; y=0.0; z=0.0;}
CVector3D(float x0, float y0, float z0)
{x=x0; y=y0; z=z0;}
};
// View reference frame class
class CViewFrame
{
public:
float step; // step size
float turn_a; // turn angle
float pitch_a; // pitch angle
float roll_a; // roll angle
CVector3D P0; // View reference point
CVector3D u; // unit vector in xv direction
CVector3D v; // unit vector in yv direction
CVector3D n; // unit vector in zv direction
void move_up(void)
{
}
void move_down(void)
{
}
void move_left(void)
{
}
void move_right(void)
{
}
void move_forward(void)
{
}
void move_backward(void)
{
}
void turn_left(void)
{
}
void turn_right(void)
{
}
void look_up(void)
{
}
void look_down(void)
{
}
void roll_left(void)
{
}
void roll_right(void)
{
}
};
CViewFrame view_frame;
int polygon_mode=0;
// 0 --- GL_FILL, 1 --- GL_LINE
int cull_face_mode=0;
// 0 --- Disable face culling, 1 --- Enable face culling
int face_to_cull=0;
// 0 --- Cull back face, 1 -- Cull front face
// Program window width and height
int pw_width, pw_height;
// Initialization function
void init(void)
{
static GLfloat light_ambient[] = { 0.01, 0.01, 0.01, 1.0 };
static GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
static GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
static GLfloat light_pos[] = {50.0, 50.0, 200.0, 0.0};
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel (GL_SMOOTH); // Set shading model
// Set light source properties for light source #0
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
glEnable(GL_LIGHTING); // Enable lighting
glEnable(GL_LIGHT0); // Enable light source #0
glEnable(GL_DEPTH_TEST); // Enable depth buffer test
glEnable(GL_NORMALIZE); // Enable auto normalization
// Enable two sided lighting
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
view_frame.P0=CVector3D(500.0, 0.0, 100.0);
view_frame.u=CVector3D(0.0, 1.0, 0.0);
view_frame.v=CVector3D(0.0, 0.0, 1.0);
view_frame.n=CVector3D(1.0, 0.0, 0.0);
view_frame.step=10;
view_frame.turn_a=PI/18;
view_frame.pitch_a=PI/18;
view_frame.roll_a=PI/6;
xrotate=0.0;
yrotate=0.0;
zrotate=0.0;
}
// Function to draw NULL-terminated string
void draw_string(void *font, char *str)
{
while((*str)!='\0') {
glutBitmapCharacter(font,(int)*str);
str++;
}
}
// Draw texts
void draw_texts(void)
{
static char *str_polygon[2]={
"Polygon Mode (Press p to change): GL_FILL",
"Polygon Mode (Press p to change): GL_LINE"};
static char *str_cull[2]={
"Face culling (Press c to change): Disabled",
"Face culling (Press c to change): Enabled"};
static char *str_face[2]={
"Face to cull (Press f to change): Back",
"Face to cull (Press f to change): Front"};
// Temporarily use orthographic projection
// and set clipping window the same as program window
glMatrixMode (GL_PROJECTION);
glPushMatrix();
glLoadIdentity ();
gluOrtho2D(0, pw_width, 0, pw_height);
glColor3f(1.0, 1.0, 1.0);
// Disable lighting before drawing texts
glDisable(GL_LIGHTING);
// Draw texts
glRasterPos2i(5, pw_height-20);
draw_string(GLUT_BITMAP_9_BY_15, str_polygon[polygon_mode]);
glRasterPos2i(5, pw_height-40);
draw_string(GLUT_BITMAP_9_BY_15, str_cull[cull_face_mode]);
glRasterPos2i(5, pw_height-60);
draw_string(GLUT_BITMAP_9_BY_15, str_face[face_to_cull]);
// Enable lighting after text drawing is finished
glEnable(GL_LIGHTING);
// Restore projection matrix
glPopMatrix();
}
// Display callback function
void display(void)
{
GLfloat mat_color[] = { 0.91, 0.53, 0.14, 1.0 };
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set material properties
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_color);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_color);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 64.0);
// Set polygon drawing mode
if (polygon_mode==0) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
else glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Set face culling mode
if (cull_face_mode==0) glDisable(GL_CULL_FACE);
else glEnable(GL_CULL_FACE);
// Define which faces to cull
if (face_to_cull==0) glCullFace(GL_BACK);
else glCullFace(GL_FRONT);
// Set matrix mode to model view
glMatrixMode (GL_MODELVIEW);
glPushMatrix(); // Save current model view matrix
CVector3D look_at;
look_at.x=view_frame.P0.x-view_frame.n.x;
look_at.y=view_frame.P0.y-view_frame.n.y;
look_at.z=view_frame.P0.z-view_frame.n.z;
gluLookAt(view_frame.P0.x, view_frame.P0.y, view_frame.P0.z,
look_at.x, look_at.y, look_at.z,
view_frame.v.x, view_frame.v.y, view_frame.v.z);
// Render two crossing cylinders
glRotatef(xrotate, 1.0, 0.0, 0.0);// rotate around x-axis
glRotatef(yrotate, 0.0, 1.0, 0.0); //rotate around y-axis
glRotatef(zrotate, 0.0, 0.0, 1.0); //rotate around z-axis
MyCylinder(40.0, 200.0, 20, 10, 5);
glRotatef(-90, 1.0, 0.0, 0.0);
glTranslatef(0,100,0);
MyCylinder(40.0, 200.0, 20, 10, 5);
glPopMatrix(); // Restore model view matrix
draw_texts(); // Draw texts
glutSwapBuffers();
}
// Reshape callback function
void reshape (int w, int h)
{
pw_width=w; pw_height=h;
// Set viewport as the entire program window
glViewport (0, 0, w, h);
// Set symmetric perspective projection
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective(60.0, (float)w/(float)h, 10.0, 100000.0);
// Reset modelview transformation matrix to identity
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
}
// Keyboard callback function
void keyboard (unsigned char key, int x, int y)
{
switch (key)
{
case 27:
exit(0); break;
// Press P to change polygon drawing mode
case 'p':
case 'P':
polygon_mode=1-polygon_mode;
glutPostRedisplay(); break;
// Press C to change face culling mode
case 'c':
case 'C':
cull_face_mode=1-cull_face_mode;
glutPostRedisplay(); break;
// Press F to choose which faces to cull
case 'f':
case 'F':
face_to_cull=1-face_to_cull;
glutPostRedisplay(); break;
case 'w':
view_frame.move_forward();
glutPostRedisplay(); break;
case 's':
view_frame.move_backward();
glutPostRedisplay(); break;
case 'a':
view_frame.move_left();
glutPostRedisplay(); break;
case 'd':
view_frame.move_right();
glutPostRedisplay(); break;
case 'q':
view_frame.roll_left();
glutPostRedisplay(); break;
case 'e':
view_frame.roll_right();
glutPostRedisplay(); break;
}
}
// Special key callback function
void special_key(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_LEFT:
view_frame.turn_left();
glutPostRedisplay(); break;
case GLUT_KEY_RIGHT:
view_frame.turn_right();
glutPostRedisplay(); break;
case GLUT_KEY_UP:
view_frame.look_up();
glutPostRedisplay(); break;
case GLUT_KEY_DOWN:
view_frame.look_down();
glutPostRedisplay(); break;
case GLUT_KEY_PAGE_UP:
view_frame.move_up();
glutPostRedisplay(); break;
case GLUT_KEY_PAGE_DOWN:
view_frame.move_down();
glutPostRedisplay(); break;
case GLUT_KEY_HOME:
xrotate+=30;
glutPostRedisplay(); break;
case GLUT_KEY_END:
yrotate+=30;
glutPostRedisplay(); break;
case GLUT_KEY_INSERT:
zrotate+=30;
glutPostRedisplay(); break;
}
}
// Main program
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize (600, 500);
glutInitWindowPosition (100, 50);
glutCreateWindow ("Draw cylinder");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutSpecialFunc(special_key);
glutMainLoop();
return 0;
}