【計算機圖形學】Cylinder rendering
阿新 • • 發佈:2021-02-14
文章目錄
本部落格基於課程"計算機圖形學",教材使用為計算機圖形學(第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
則是用於繪製填充三角形,填充三角形(以第一個點為頂點,之後每兩個點合起來圍成的三角形進行填充,相鄰的點之間填充
-
在繪製多個四邊長條時,由於迴圈設定的原因,會出現多了一個圓環的現象(如右圖),
~所以需要加入一個~直接修改if (nowRadius + unitRadius <= radius)
,然後再進行放置nowRadius + unitRadius圓環for
迴圈的退出條件即可
實現程式碼
核心程式碼及關鍵步驟註釋
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();
}
全部程式碼
// ====== 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;
}