第四章 圖形的表示與資料結構
如何在電腦中建立恰當的模型表示不同繪圖物件。
如何組織繪圖物件的描述資料以使儲存這些資料所要的空間最省,檢索、處理這些資料的速度較快。
基本概念——幾何資訊與拓撲資訊
剛體運動:不改變圖形上任意兩點間的距離,也不改變圖形的幾何性質的運動。
拓撲運動:允許形體作彈性運動,即在拓撲關係中,對圖形可隨意地伸張扭曲。但圖上各個點仍為不同的點,決不允許把不同的點合并成一個點。
基本概念——座標系
建模座標系(Modeling Coordinate System)
使用者座標系
觀察座標系(Viewing Coordinate System)
規格化裝置座標系(Normalized Device coordinate System)
裝置座標系(Device Coordinate System)
基本概念-實體
點的領域:如果P是點集S的一個元素,那麼點P的以R(R>0)為半徑的領域指的是圍繞點P的半徑為R的小球(二維情況下為小圓)。
開集的閉包:是指該開集與其所有邊界點的集合并集,本身是一個閉集。
正則集:由內部點構成的點集的閉包就是正則集,三維空間的正則集就是正則形體。
組成三維物體的點的集合可以分為兩類:
內點為點集中的這樣一些點,它們具有完全包含於該點集的充分小的領域。
邊界點:不具備此性質的點集中的點。
二維流形指的是對於實體表面上的任意一點,都可以找到一個圍繞著它的任意小的領域,該領域與平面上的一個圓盤是拓撲等價的。
實體:對於一個佔據有限空間的正則形體,如果其表面是二維流形,則該正則形體為實體。
基本概念——正則集合運算
有效實體的封閉性。
把能夠產生正則形體的集合運算稱為正則集合運算。
基本概念——平面多面體與歐拉公式
歐拉公式證明簡單多面體的頂點數V、邊數E和面數F滿足如下關係:V-E+F=2。
非簡單多面體需對歐拉公式加以擴充。令H表示多面體表面上孔的個數,G表示貫穿多面體的孔的個數,C表示獨立的、不相串連的多面體數,則擴充後的歐拉公式為:V-E+F-H=2(C-G)。
4.2 三維形體的表示
線框模型與實體模型(實體造型技術)
可以將實體模型的表示大致分為三類:
邊界表示(Boundary representation, B-reps)
構造實體幾何表示
空間分割(Space-partitioning)表示
多邊形表面模型:
邊界表示(B-reps)的最普遍方式是多邊形表面模型,它使用一組包圍物體內部的平面多邊形,也即平面多面體,來描述實體。
多邊形表面模型——資料結構
幾何資訊:
建立3張表:頂點表、邊表和多邊形表來儲存幾何資料。
實體模型中,用多邊形頂點座標值以及多邊形所在平面方程方式儲存實體單個表面部分的空間方向資訊
拓撲資訊:翼邊結構表示(Winged Edges Structure)
屬性資訊:用屬性工作表來儲存多邊形面的屬性,指明物體透明度及表面反射度的參數和紋理特徵等等。
多邊形網格:三維形體的邊界通常用多邊形網格(polygon mesh)的拼接來類比。
掃描表示:
掃描標記法(sweep representation)可以利用簡單的運動規則產生有效實體。
包含兩個要素
一是作掃描運動的基本圖形(截面);
二是掃描運動的方式。
構造實體幾何法
構造實體幾何法(CSG,Constructive Solid Geometry)由兩個實體間的並、交或差操作產生新的實體。
在構造實體幾何法中,集合運算的實現過程可以用一棵二叉樹(稱為CSG樹)來描述。
樹的葉子是基本體素或是幾何變換參數;
樹的非終端結點是施加於其子結點的正則集合運算元(正則並、正則交和正則差)或幾何變換的定義。
優點:如果體素設定比較齊全,通過集合運算就可以構造出多種不同的符合需要的實體。
缺點一:集合運算的中間結果難以用簡單的代數方程表示,求交困難。
缺點二:CSG樹不能顯式地表示形體的邊界,因而無法直接顯示CSG樹表示的形體。
解決:光線投射演算法
空間位置枚舉表示
空間位置枚舉標記法將包含實體的空間分割為大小相同、形狀規則(正方形或立方體)的體素,然後,以體素的集合來表示繪圖物件。
二維情況,常用二維數組存放。
三維情況下,常用三維數組p[i][j][k]來存放。
八叉樹
八叉樹(octrees)又稱為分層樹結構,它對空間進行自適應劃分,採用具有階層的八叉樹來表示實體。
BSP樹
二叉空間分割(Binary Space Partitioning,BSP)樹方法是一種類似於八叉樹的空間分割方法,它每次將一實體用任一位置和任一方向的平面分為二部分(不同於八叉樹方法的每次將實體用平行於笛卡爾座標平面的三個兩兩垂直的平面分割)。
OpenGL中的實體模型函數
GLUT庫中的多面體函數:
繪製實體或線框球面
void glutSolidSphere/glutWireSphere (GLdouble radius, GLint slices, GLint stacks);
繪製實體或線框圓錐面
void glutSolidCone/glutWireCone (GLdouble radius, GLdouble height, GLint slices, GLint stacks);
繪製實體或線框圓環
void glutSolidTorus/ glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint slices,GLint stacks);
繪製實體或線框茶壺
void glutSolidTeapot/glutWireTeapot (GLdouble size);
定義一個二次曲面
GLUquadricObj *sphere;
啟用二次曲面繪製器
sphere = gluNewQuadric( );
指定二次曲面的繪製方式
gluQuadricDrawStyle(sphere, GLU_LINE);
繪製二次曲面
gluSphere(sphere, radius, slices, stacks);
gluCylinder(sphere,baseRadius,topRadius, height, slices, stacks);
gluDisk(sphere,innerRadius,outerRadius, slices, stacks);
4.4 層次模型
段與層次模型:
具有邏輯意義的有限個圖素(或體素)及其附加屬性的集合稱為段,或者稱為圖段(二維空間中)、結構和對象。
段是可以嵌套
段與基本圖形元素的區別在於,基本圖形元素是用資料來描述的,而段是用規則來描述的。
段一般具有三個特性:可見度、醒目性和可選擇性(可由互動式輸入裝置來選擇)。
利用段的嵌套來構造複雜的對象或系統。
儲存簡單:一個段雖然在圖中各處出現,但他的幾何和拓撲資訊只要儲存一次。
編輯簡單:刪除、移動及縮放操作都可以以段為單位。
層次模型的實現:
系統的層次模型可以通過將一個圖段嵌套到另一個圖段中形成圖段樹來建立。不同的段和基本圖形元素在各自的建模座標系中定義。
圖層。通過把功能相同的部分歸類,並將它們繪製在同一層上,有助於圖形的理解和管理。
一般圖層不再嵌套。
OpenGL中的層次模型
顯示列表的建立
glNewList( listID, listMode );
glutSolidCube(2.0);
……
glEndList();
顯示列表的執行
void glListBase(GLuint offsetValue);
多級顯示列表
OpenGL支援建立多級顯示列表,即在glNewList和glEndLsit函數對之間允許調用glCallList函數來執行其他顯示列表。
顯示列表的刪除
void glDeleteLists(GLuint listID, GLsizei range);
#include <gl/glut.h>
static GLsizei iMode = 1;
static GLfloat xRot = 0.0f; //x方向旋轉參數
static GLfloat yRot = 0.0f; //y方向旋轉參數
GLUquadricObj *obj; //二次曲面對象
void Initial(void)
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glColor3f(0.0f, 0.0f, 0.0f);
obj = gluNewQuadric( );
gluQuadricDrawStyle(obj, GLU_LINE); //以線框方式繪製二次曲面對象
}
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D (-1.5f, 1.5f, -1.5f, 1.5f);
}
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(xRot, 1.0f, 0.0f, 0.0f); //旋轉圖形
glRotatef(yRot, 0.0f, 1.0f, 0.0f); //旋轉圖形
//指定需要繪製的圖元
switch(iMode) {
case 1:
glutWireTetrahedron(); break;
case 2:
glutSolidTetrahedron(); break;
case 3:
glutWireOctahedron(); break;
case 4:
glutSolidOctahedron(); break;
case 5:
glutWireSphere(1.0f,15,15); break;
case 6:
glutSolidSphere(1.0f,15,15); break;
case 7:
glutWireTeapot(1.0f); break;
case 8:
glutSolidTeapot(1.0f); break;
case 9:
gluSphere(obj, 1.0f, 15, 15); break;
case 10:
gluCylinder(obj,1.0f,0.0f,1.0f,15,15); break;
case 11:
gluPartialDisk(obj,0.3f,0.8f,15,15,30.0f,260.0f); break;
default: break;
}
glFlush();
}
void ProcessMenu(int value)
{
iMode = value;
glutPostRedisplay();
}
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) xRot-= 5.0f;
if(key == GLUT_KEY_DOWN) xRot += 5.0f;
if(key == GLUT_KEY_LEFT) yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT) yRot += 5.0f;
if(xRot > 356.0f) xRot = 0.0f;
if(xRot < -1.0f) xRot = 355.0f;
if(yRot > 356.0f) yRot = 0.0f;
if(yRot < -1.0f) yRot = 355.0f;
glutPostRedisplay();
}
int main(int argc, char* argv[])
{
int nGlutPolyMenu=0;
int nGlutCurveMenu=0;
int nGluCurveMenu=0;
int nMainMenu=0;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(400,400);
glutInitWindowPosition(100,100);
glutCreateWindow("OpenGL模型繪製函數樣本");
//建立菜單並定義菜單回呼函數
nGlutPolyMenu = glutCreateMenu(ProcessMenu);
glutAddMenuEntry("線框正四面體",1); //建立GLUT多面體繪製菜單
glutAddMenuEntry("實體正四面體",2);
glutAddMenuEntry("線框正八面體",3);
glutAddMenuEntry("實體正八面體",4);
nGlutCurveMenu = glutCreateMenu(ProcessMenu); //建立GLUT曲面繪製菜單
glutAddMenuEntry("線框球面",5);
glutAddMenuEntry("實體球面",6);
glutAddMenuEntry("線框茶壺",7);
glutAddMenuEntry("實體茶壺",8);
nGluCurveMenu = glutCreateMenu(ProcessMenu); //建立GLU曲面繪製菜單
glutAddMenuEntry("線框球面",9);
glutAddMenuEntry("線框圓錐面",10);
glutAddMenuEntry("線框圓環面",11);
nMainMenu = glutCreateMenu(ProcessMenu); //建立主菜單
glutAddSubMenu("GLUT多面體", nGlutPolyMenu);
glutAddSubMenu("GLUT曲面", nGlutCurveMenu);
glutAddSubMenu("GLU曲面", nGluCurveMenu);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutDisplayFunc(Display);
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
Initial();
glutMainLoop();
return 0;
}
#include <gl/glut.h>
GLuint OlympicRings;
void Initial(void)
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
OlympicRings = glGenLists(1);
glNewList(OlympicRings, GL_COMPILE);
glColor3f(1.0, 1.0, 0.0);
glTranslatef(-22.0, 0.0, 0.0);
glutSolidTorus(0.5, 20.0, 15, 50); //繪製黃色環
glColor3f(0.0, 1.0, 0.0);
glTranslatef(44.0, 0.0, 0.0);
glutSolidTorus(0.5, 20.0, 15, 50); //繪製綠色環
glColor3f(0.0, 0.0, 0.0);
glTranslatef(-22.0, 30.0, 0.0);
glutSolidTorus(0.5, 20.0, 15, 50); //繪製黑色環
glColor3f(0.0, 0.0, 1.0);
glTranslatef(-42.0, 0.0, 0.0);
glutSolidTorus(0.5, 20.0, 15, 50); //繪製藍色環
glColor3f(1.0, 0.0, 0.0);
glTranslatef(84.0, 0.0, 0.0);
glutSolidTorus(0.5, 20.0, 15, 50); //繪製紅色環
glEndList();
}
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D (-70.0f, 70.0f, -70.0f, 70.0f);
}
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glCallList(OlympicRings); //調用顯示列表
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(400,400);
glutInitWindowPosition(100,100);
glutCreateWindow("OpenGL模型繪製函數樣本");
glutDisplayFunc(Display);
glutReshapeFunc(ChangeSize);
Initial();
glutMainLoop();
return 0;
}