计算机图形学 - 实验:Cylinder rendering

本博客基于课程"计算机图形学",教材使用为计算机图形学(第4版) [Computer Graphics with OpenGL, Fourth Edition],部分代码模板便来自于此教材,并且有所改动

实验思路

代码思路

  1. drawsurface()unitSlice:每次增加的圆周块,unitHeight:每次增加的高度块。总体绘制路线是,在每个圆周块上,绘制完整个高度的块,然后继续绘制下一个圆周块,知道圆周块组成一个圆。所以第一个for循环用来依次增加圆周块的位置,第二个循环用来依次增加高度。每个圆柱以原点为中心,所以第二个循环的起点为正的高度的一半,终点为负的高度的一半。每次放置像素点时,为了实现光照,先用glNormal3f设定法向量,xy坐标值与像素点保持一致,z坐标始终为0,并且为了可以实现GL_QUAD_STRIP绘制模式效果,需要在同一循环中放置圆周上相邻的两个像素点
  2. drawDisk():变量unitSlicedrawsurface()函数中的,unitRadius是各个同心圆(三角)的半径增长单位,首先绘制最中心的一圈三角形,使用GL_TRIANGLE_FAN绘制模式,也就是绘制一圈距离初始点(0,0,0)距离unitRadius的像素点。接着用GL_QUAD_STRIP绘制模式绘制多个四边长条,需要用到两个循环,一个用来对半径递增,一个用来对相同半径的环进行像素点放置

问题及解决方法

  1. GL_QUAD_STRIP绘制模式中,其绘制功能是填充面(每两个点构成一条线,每两个线构成一个四边形),绘制四边形的绕法是N型,并不是简单的顺时针型,在此实验中,放置点的顺序是:右下点->左下点->右上点->左上点。所以在绘制侧面时,循环的初始值和终止值可以是nstack/2-nstack,也可以互换,但是互换之后需要调换放置像素点ii+1先后顺序

  2. GL_TRIANGLE_FAN则是用于绘制填充三角形,填充三角形(以第一个点为顶点,之后每两个点合起来围成的三角形进行填充,相邻的点之间填充),所以在创建第一个圆中的多个三角形时,需要先在原点放置一个像素才能使之连起来
    GL_QUAD_STRIP绘制模式示意图GL_TRIANGLE_FAN绘制模式示意图

  3. 在绘制多个四边长条时,由于循环设置的原因,会出现多了一个圆环的现象(如右图),在这里插入图片描述

复制
# 实现代码
## 核心代码及关键步骤注释
```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;
}
```
posted @   ChisocDust  阅读(159)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示