计算机图形学 - 实验:Cylinder rendering
本博客基于课程"计算机图形学",教材使用为计算机图形学(第4版) [Computer Graphics with OpenGL, Fourth Edition],部分代码模板便来自于此教材,并且有所改动
实验思路
代码思路
drawsurface()
:unitSlice
:每次增加的圆周块,unitHeight
:每次增加的高度块。总体绘制路线是,在每个圆周块上,绘制完整个高度的块,然后继续绘制下一个圆周块,知道圆周块组成一个圆。所以第一个for
循环用来依次增加圆周块的位置,第二个循环用来依次增加高度。每个圆柱以原点为中心,所以第二个循环的起点为正的高度的一半,终点为负的高度的一半。每次放置像素点时,为了实现光照,先用glNormal3f
设定法向量,x
和y
坐标值与像素点保持一致,z
坐标始终为0,并且为了可以实现GL_QUAD_STRIP
绘制模式效果,需要在同一循环中放置圆周上相邻的两个像素点drawDisk()
:变量unitSlice
同drawsurface()
函数中的,unitRadius
是各个同心圆(三角)的半径增长单位,首先绘制最中心的一圈三角形,使用GL_TRIANGLE_FAN
绘制模式,也就是绘制一圈距离初始点(0,0,0)
距离unitRadius
的像素点。接着用GL_QUAD_STRIP
绘制模式绘制多个四边长条,需要用到两个循环,一个用来对半径递增,一个用来对相同半径的环进行像素点放置
问题及解决方法
-
GL_QUAD_STRIP
绘制模式中,其绘制功能是填充面(每两个点构成一条线,每两个线构成一个四边形),绘制四边形的绕法是N型,并不是简单的顺时针型,在此实验中,放置点的顺序是:右下点->左下点->右上点->左上点。所以在绘制侧面时,循环的初始值和终止值可以是nstack/2
和-nstack
,也可以互换,但是互换之后需要调换放置像素点i
和i+1
先后顺序 -
GL_TRIANGLE_FAN
则是用于绘制填充三角形,填充三角形(以第一个点为顶点,之后每两个点合起来围成的三角形进行填充,相邻的点之间填充),所以在创建第一个圆中的多个三角形时,需要先在原点放置一个像素才能使之连起来
-
在绘制多个四边长条时,由于循环设置的原因,会出现多了一个圆环的现象(如右图),
复制# 实现代码 ## 核心代码及关键步骤注释 ```cpp void drawsurface(float radius, float height, int nslice, int nstack) // nslice --- Number of subdivision around z-axis // nstack --- Number of subdivision along z-axis { //Write your code here const float unitSlice = 2 * PI / nslice; const float unitHeight = 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(unitSlice * (i + 1)), radius * sin(unitSlice * (i + 1)), 0); glVertex3f(radius * cos(unitSlice * (i + 1)), radius * sin(unitSlice * (i + 1)), unitHeight * j); glNormal3f(radius * cos(unitSlice * i), radius * sin(unitSlice * i), 0); glVertex3f(radius * cos(unitSlice * i), radius * sin(unitSlice * i), unitHeight * 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); // Draw quads //Write your code here const float unitSlice = 2 * PI / nslice; const float unitRadius = radius / nring; glBegin(GL_TRIANGLE_FAN); glVertex3f(0, 0, 0); for (int i = 0; i < nslice; i++) { glVertex3f(unitRadius * cos(unitSlice * i), unitRadius * sin(unitSlice * i), 0); } glEnd(); // Draw triangles around center //Write your code here glBegin(GL_QUAD_STRIP); float nowRadius = 0; for (int i = 0; i < nring; i++) { for (int j = 0; j <= nslice; j++) { glVertex3f(nowRadius * cos(unitSlice * j), nowRadius * sin(unitSlice * j), 0); if (nowRadius + unitRadius <= radius) glVertex3f((nowRadius + unitRadius) * cos(unitSlice * j), (nowRadius + unitRadius) * sin(unitSlice * j), 0); } nowRadius += unitRadius; } glEnd(); } ``` ## 全部代码 ```cpp // ====== 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 <GL/glut.h> #include <math.h> #include <windows.h> #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 { //Write your code here const float unitSlice = 2 * PI / nslice; const float unitHeight = 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(unitSlice * (i + 1)), radius * sin(unitSlice * (i + 1)), 0); glVertex3f(radius * cos(unitSlice * (i + 1)), radius * sin(unitSlice * (i + 1)), unitHeight * j); glNormal3f(radius * cos(unitSlice * i), radius * sin(unitSlice * i), 0); glVertex3f(radius * cos(unitSlice * i), radius * sin(unitSlice * i), unitHeight * 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); // Draw quads //Write your code here const float unitSlice = 2 * PI / nslice; const float unitRadius = radius / nring; glBegin(GL_TRIANGLE_FAN); glVertex3f(0, 0, 0); for (int i = 0; i < nslice; i++) { glVertex3f(unitRadius * cos(unitSlice * i), unitRadius * sin(unitSlice * i), 0); } glEnd(); // Draw triangles around center //Write your code here glBegin(GL_QUAD_STRIP); float nowRadius = 0; for (int i = 0; i < nring; i++) { for (int j = 0; j <= nslice; j++) { glVertex3f(nowRadius * cos(unitSlice * j), nowRadius * sin(unitSlice * j), 0); if (nowRadius + unitRadius <= radius) glVertex3f((nowRadius + unitRadius) * cos(unitSlice * j), (nowRadius + unitRadius) * sin(unitSlice * j), 0); } nowRadius += unitRadius; } 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(800, 800); glutInitWindowPosition(100, 50); glutCreateWindow("Draw cylinder"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutSpecialFunc(special_key); glutMainLoop(); return 0; } ```
标签:
计算机图形学
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律