图形学实验报告

更新时间:2024-04-08 19:38:05 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

计 算 机 图 形 学

实 验 指 导

学号:1441901105 姓名:谢卉

实验一:图形的几何变换

实验学时:4学时 实验类型:验证 实验要求:必修 一、实验目的

二维图形的平移、缩放、旋转和投影变换(投影变换可在实验三中实现)等是最基本的图形变换,被广泛用于计算机图形学的各种应用程序中,本实验通过算法分析以及程序设计实验二维的图形变换,以了解变换实现的方法。如可能也可进行裁剪设计。 二、实验内容

掌握平移、缩放、旋转变换的基本原理,理解线段裁剪的算法原理,并通过程序设计实现上述变换。建议采用VC++实现OpenGL程序设计。 三、实验原理、方法和手段

1.

图形的平移

在屏幕上显示一个人或其它物体(如图1所示),用交互操作方式使其在屏幕上沿水平和垂直方向移动Tx和Ty,则有

x’=x+Tx y’=y+Ty

其中:x与y为变换前图形中某一点的坐标,x’和y’为变换后图形中该点的坐标。其交互方式可先定义键值,然后操作功能键使其移动。

2.

图形的缩放

在屏幕上显示一个帆船(使它生成在右下方),使其相对于屏幕坐标原点缩小s倍(即x方向和y方向均缩小s倍)。则有:

x’=x*s y’=y*s

注意:有时图形缩放并不一定相对于原点,而是事先确定一个参考位置。一般情况下,参考点在图形的左下角或中心。设参考点坐标为xf、yf则有变换公式

x’=x*Sx+xf*(1-Sx)=xf+(x-xf)*Sx y’=y*Sy+yf*(1-Sy)=yf+(y-yf)*Sy

式中的x与y为变换前图形中某一点的坐标,x’和y’为变换后图形中该点的坐标。当Sx>1和Sy>1时为放大倍数,Sx<1和Sy<1时为缩小倍数(但Sx和Sy

必须大于零)。

3.

图形的旋转

在屏幕上显示一个汽车,根据自己确定的旋转角度和旋转中心对图形进行旋转。旋转公式为

x’=xf+(x-xf)*cos(angle)-(y-yf)*sin(angle) y’=yf+(y-yf)*cos(angle)+(x-xf)*sin(angle)

其中:xf,yf为围绕旋转的中心点的坐标。x,y为旋转前图形中某点的坐标,x’和y’为旋转后图形中该点的坐标。

4.

裁剪

对一个三角形进行裁剪,裁剪后的图形应是一个封闭的图形。可采用线段裁剪法,其方法可用书上的线段相交求点的公式,确定可见线段予以保存,不在窗口的线段则应舍弃。

图1

四、实验组织运行要求

本实验采用集中授课形式,每个同学独立完成上述实验要求。 五、实验条件

每人一台计算机独立完成实验。 六、实验步骤

(1) 将图形显示在初始位置。

(2) 对图形各点按变换表达式作坐标变换,计算出各点变换后的相应点的

坐标。

(3) 将原来的图形抹去。 (4) 在新的位置显示图形。

七、程序代码

平移缩放

// test1.cpp : 定义控制台应用程序的入口点。 //

#include\ #include\ #include\ #include float width,highth,angle; void init (void)//画正方形 {

glClearColor (1.0,1.0, 1.0, 0.0);//背景颜色

glMatrixMode (GL_PROJECTION);// 投影

gluOrtho2D (0.0, 600.0, 0.0, 600.0);//参数分别代表(左下角x坐标,右上角x坐标,左下角y坐标,右上角y坐标) }

void display (void) {

glClear (GL_COLOR_BUFFER_BIT);

glColor3f (0.0, 1.0, 1.0);//矩形颜色

glBegin(GL_POLYGON);

glTranslatef(0,0,0);

glRotatef(angle,0,0,1);

glVertex2f(100.0f+width,100.0f+highth);//用来画点

glVertex2f(100.0f+width,300.0f+highth);

glVertex2f(300.0f+width,300.0f+highth);

glVertex2f(300.0f+width,100.0f+highth);

glEnd();

glFlush ( ); }

void mySpecialKeyboard(intkey, intx, inty) {

if(key==GLUT_KEY_RIGHT)

width+=5;

if(key==GLUT_KEY_LEFT)

width-=5;

if(key==GLUT_KEY_UP)

highth+=5;

if(key==GLUT_KEY_DOWN)

highth-=5;

glutPostRedisplay(); }

void myKeyboard(unsignedcharkey, intx, inty) {

if(key == 'c' || key == 'C')

exit(0);

glutPostRedisplay(); }

void mymouse(intbutton,intstate,intx,inty)//鼠标控制缩放

{

if(state==GLUT_DOWN) {

if(button==GLUT_LEFT_BUTTON) {

glScalef(0.5,0.5,0.0); display();

}

elseif(button==GLUT_RIGHT_BUTTON) {

glScalef(1.5,1.5,0.0);

display();

}

glutPostRedisplay();//重新调用绘制函数 } return; }

void main (intargc, char** argv) {

glutInit (&argc, argv);

glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

glutInitWindowPosition (50, 50);

glutInitWindowSize (600, 600);

glutCreateWindow (\方向键控制平移,鼠标控制缩放\);

init ( );

glutDisplayFunc (display);

glutSpecialFunc( mySpecialKeyboard);

glutMouseFunc(&mymouse); glutKeyboardFunc( myKeyboard); glutMainLoop ( );

}

旋转

#include\ #include #include #include\

#defineDEG_TO_RAD 0.017453 //角度转为弧度的参数,即 2*PI/360 float theta=30.0; //直线与X轴正方向的夹角 float length=200.0; //直线的长度

float x=300.0, y=200.0; //直线的第一个端点 void init (void) { }

void display (void) {

glClear (GL_COLOR_BUFFER_BIT); glColor3f (0.0, 1.0, 1.0); glBegin (GL_POLYGON); glVertex2f (x, y);

glVertex2f ( x + length*cos(DEG_TO_RAD*theta), y + length*sin(DEG_TO_RAD*theta) ); glVertex2f ( x + length*cos(DEG_TO_RAD* (theta+30) ), y + length*sin(DEG_TO_RAD* glEnd ( );

glutSwapBuffers ( ); //交换前后台缓存 glClearColor (1.0, 1.0, 1.0, 0.0); glMatrixMode (GL_PROJECTION); gluOrtho2D (0.0, 640.0, 0.0, 480.0);

(theta+30)) );

}

/*void idleFunc() { }*/

void myKeyboard(unsignedcharkey, intx, inty) {

if(key == 'a' || key == 'A')

theta += 5.0; theta -= 5.0;

if(key == 's' || key == 'S') theta += 0.1;

if (theta>360) theta -=360;

glutPostRedisplay(); //重新调用绘制函数

if(key == 'c' || key == 'C')

exit(0);

if (theta>360) theta -=360;

if (theta<0) theta +=360;

glutPostRedisplay(); //重新调用绘制函数 }

void main (intargc, char** argv) {

glutInit (&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); glutInitWindowPosition (100, 100); glutInitWindowSize (640, 480); glutCreateWindow (\键左转,S键右转\);

init ( ); glutDisplayFunc (display); glutKeyboardFunc( myKeyboard);

// glutIdleFunc(idleFunc); //指定空闲回调函数 glutMainLoop ( );

}

八、实验结果

实验二:图形的区域填充

实验学时:4学时 实验类型:验证 实验要求:必修 一、实验目的

区域填充是指先将区域内的一点(常称为种子点)赋予给定颜色,然后将这种颜色扩展到整个区域内的过程。区域填充技术广泛应用于交互式图形、动画和美术画的计算机辅助制作中。本实验采用递归填充算法或扫描线算法实现对光栅图形的区域填充。通过本实验,可以掌握光栅图形编程的基本原理和方法。 二、实验内容

掌握光栅图形的表示方法,实现种子算法或扫描线算法。通过程序设计实现上述算法。建议采用VC++实现OpenGL程序设计。 三、实验原理、方法和手段

1. 递归算法

在要填充的区域内取一点(X,Y)的当前颜色记为oldcolor,用要填充的颜色newcolor去取代,递归函数如下:

procedure flood-fill(X,Y,oldcolor,newcolor:integer); begin

if getpixel(framebuffer,x,y)=oldcolor then begin

setpixel(framebuffer,x,y,newcolor); flood-fill(X,Y+1,oldcolor,newcolor); flood-fill(X,Y-1,oldcolor,newcolor); flood-fill(X-1,Y,oldcolor,newcolor); flood-fill(X+1,Y,oldcolor,newcolor); end

end 2. 扫描线算法

扫描线算法的效率明显高于递归算法,其算法的基本思想如下:

(1)(初始化)将算法设置的堆栈置为空,将给定的种子点(x,y)压入堆栈。 (2)(出栈)如果堆栈为空,算法结束;否则取栈顶元素(x,y)作为种子点。 (3)(区段填充)从种子点(x,y)开始沿纵坐标为y的当前扫描线向左右两个

方向逐个象素进行填色,其值置为newcolor,直到抵达边界为止。 (4()定范围)以xleft和xright分别表示在步骤3中填充的区段两端点的横坐标。 (5)(进栈)分别在与当前扫描线相邻的上下两条扫描线上,确定位于区间[xleft,

xright]内的给定区域的区段。如果这些区段内的象素的颜色值为newcolor或者boundarycolor(边界上象素的颜色值),则转到步骤2,否则取区段的右端点为种子压入堆栈,再转到步骤2继续执行。 四、实验组织运行要求

本实验采用集中授课形式,每个同学独立完成上述实验要求。 五、实验条件

每人一台计算机独立完成实验。 六、实验步骤

(1) 将图形显示在初始位置。 (2) 给定种子点的坐标。

(3) 显示从种子点开始的扩散过程。 (4) 显示填充后的图形。 七、程序代码

种子扫描线算法

using System;

using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text;

using System.Windows.Forms;

using System.Runtime.InteropServices;// using System.Threading;

namespace ClipLine {

publicpartialclassFrmMain : Form {

[DllImport(\)]

privatestaticexternint SetPixel(IntPtr hdc, int x1, int y1, int color); [DllImport(\)]

privatestaticexternuint GetPixel(IntPtr hdc, int XPos, int YPos); [DllImport(\)]

privatestaticexternuint GetPixel(IntPtr hdc, Point p); [DllImport(\)]

publicstaticexternInt32 ReleaseDC(IntPtr hwnd, IntPtr hdc); public FrmMain() {

InitializeComponent(); }

List mcp = newList(); List mwp = newList(); Graphics g; //画布 Point lastp; Point mSeed;

int Flag; //选择绘制图形类型(窗口或线段) //初始化

privatevoid FrmMain_Load(object sender, EventArgs e) {

g =Graphics.FromHwnd(picShow.Handle);

picShow.Cursor = System.Windows.Forms.Cursors.Cross; Flag = 1;

btnFillColor.BackColor = Color.Green; }

privatevoid picShow_MouseDown(object sender, MouseEventArgs e) {

lastp.X = e.X; lastp.Y = e.Y; if (Flag == 1) {

mwp.Add(lastp); if (mwp.Count > 1) {

g.DrawLine(Pens.Black, mwp[mwp.Count - 2], mwp[mwp.Count - 1]); } } elseif (Flag == 2)

{

mcp.Add(lastp); if (mcp.Count > 1) {

g.DrawLine(Pens.Blue, mcp[mcp.Count - 2], mcp[mcp.Count - 1]); } } else

{

mSeed = newPoint(e.X, e.Y);

MessageBox.Show(\已选择种子,可以开始扫描线种子填充了!\,\提示\); } } //绘制裁剪窗口

privatevoid btnDrawWindow_Click(object sender, EventArgs e) {

Flag = 1; mwp.Clear(); //g.Clear(Color.White);

g.DrawRectangle(Pens.Black, 10, 10, picShow.Width - 25, picShow.Height - 25); } //执行裁剪操作

privatevoid btnClip_Click(object sender, EventArgs e) {

//ClipPolygon(mcp, mwp); if(mwp.Count>2)

FillPolygon(btnFillColor.BackColor); }

//求直线方程(斜率和截距)

privatevoid SolveLine(Point p1,Point p2,reffloat k,reffloat b) {

if (p1.X != p2.X) {

k =(float) (p1.Y - p2.Y) / (p1.X - p2.X); b = p1.Y - k * p1.X; } } //裁剪多边形

privatevoid ClipPolygon(List cp,List wp) {

List tp=newList(); float k1=0, b1=0, k2=0, b2=0; Point s, p,t,p1,p2; Point cwp = newPoint();

bool IsTrue;

for (int i = 0; i < wp.Count; i++) {

p1 = wp[i]; if (i == wp.Count - 1) {

p2 = wp[0]; t=wp[1]; } else

{

p2 = wp[i + 1]; if (i == wp.Count - 2) {

t = wp[0]; } else

{

t = wp[i + 2]; } } //计算窗口边的斜率和截距

SolveLine(p1, p2, ref k1, ref b1); if (p1.X != p2.X) {

#region if (t.Y >= k1 * t.X + b1) {

IsTrue = true; } else

{

IsTrue = false; } //用此边裁剪多边形

for (int j = 0; j < cp.Count; j++) {

s = cp[j]; if (j == cp.Count - 1) {

p = cp[0]; } else

{

p = cp[j + 1]; } //先判断线段是否穿过窗口

if ((k1 * s.X + b1 - s.Y) * (k1 * p.X + b1 - p.Y) < 0) {

SolveLine(s, p, ref k2, ref b2); //求解交点 if (p1.X == p2.X)

{

cwp.X = p1.X;

cwp.Y = Convert.ToInt16(k2 * p1.X + b2); } elseif (s.X == p.X)

{

cwp.X = s.X;

cwp.Y = Convert.ToInt16(k1 * s.X + b1); } else

{

cwp.X = Convert.ToInt16((b2 - b1) / (k1 - k2)); cwp.Y = Convert.ToInt16(k2 * cwp.X + b2);

}

tp.Add(cwp); } //

if ((p.Y >= k1 * p.X + b1) == IsTrue) {

tp.Add(p); } }

#endregion } else

{

#region if (t.X >=p1.X) {

IsTrue = true; } else

{

IsTrue = false; }

//用此边裁剪多边形

for (int j = 0; j < cp.Count; j++) {

s = cp[j]; if (j == cp.Count - 1) {

p = cp[0]; } else

{

p = cp[j + 1]; } //先判断线段是否穿过窗口

if ((s.X - p1.X) * (p.X - p1.X) < 0) {

SolveLine(s, p, ref k2, ref b2); //求解交点 if (p1.X == p2.X)

{

cwp.X = p1.X;

cwp.Y = Convert.ToInt16(k2 * p1.X + b2); } elseif (s.X == p.X)

{

cwp.X = s.X;

cwp.Y = Convert.ToInt16(k1 * s.X + b1); } else

{

cwp.X = Convert.ToInt16((b2 - b1) / (k1 - k2)); cwp.Y = Convert.ToInt16(k2 * cwp.X + b2);

}

tp.Add(cwp); } //

if ((p.X >= p1.X) == IsTrue) {

tp.Add(p); } }

#endregion } //

cp.Clear(); cp.AddRange(tp); tp.Clear(); }

Point[] ps = newPoint[cp.Count]; //绘制图形

for (int i = 0; i < cp.Count; i++) {

ps[i] = cp[i]; }

Graphics g= picShow.CreateGraphics(); if (cp.Count > 0)

g.FillPolygon(Brushes.Red, ps); } //绘制多边形

privatevoid btnDrawPolygon_Click(object sender, EventArgs e) {

Flag = 2; mcp.Clear(); }

privatevoid picShow_MouseDoubleClick(object sender, MouseEventArgs e) { if (Flag == 1) {

g.DrawLine(Pens.Black, mwp[mwp.Count - 1], mwp[0]); mwp.RemoveAt(mwp.Count - 1); //FillPolygon(); } elseif(Flag==2) {

g.DrawLine(Pens.Blue, mcp[mcp.Count - 1], mcp[0]); mcp.RemoveAt(mcp.Count - 1); } }

privatevoid btnClear_Click(object sender, EventArgs e) {

mwp.Clear();

g.Clear(Color.White); }

privatevoid btnRectant_Click(object sender, EventArgs e) {

mwp.Clear(); //g.Clear(Color.White); Point[] wps = newPoint[4]; wps[0].X = 150; wps[0].Y = 100; wps[1].X = 400; wps[1].Y = 100; wps[2].X = 400; wps[2].Y = 300; wps[3].X = 150; wps[3].Y = 300; mwp.AddRange(wps);

g.DrawPolygon(Pens.Black, wps);

g.DrawRectangle(Pens.Black,10, 10, picShow.Width-25 , picShow.Height-25 ); }

//求多边形的包络线

privatevoid PloygonEnvelope(refPoint minP,refPoint maxP) {

minP.X = mwp[0].X; minP.Y = mwp[0].Y; maxP.X = mwp[0].X; maxP.Y = mwp[0].Y; for (int i =1; i < mwp.Count; i++) {

if (mwp[i].X < minP.X) {

minP.X = mwp[i].X; } if (mwp[i].Y < minP.Y) {

minP.Y = mwp[i].Y; } if (mwp[i].X > maxP.X) {

maxP.X = mwp[i].X; } if (mwp[i].Y > maxP.Y) {

maxP.Y = mwp[i].Y; } }

//g.DrawRectangle(Pens.Blue,minP.X, minP.Y, maxP.X - minP.X, maxP.Y - minP.Y);//绘制包络线 }

//判断点是否在多边形内

privatebool IsInPolygon(Point p) { int nCross=0;

Point p1 = newPoint(); Point p2 = newPoint(); Point pt = newPoint();

for (int i = 0; i < mwp.Count - 1; i++) {

p1 = mwp[i]; p2 = mwp[i + 1];

if (((p.X >= p1.X) && (p.X < p2.X)) || ((p.X >= p2.X) && (p.X < p1.X))) {

pt.Y= p1.Y + (p.X - p1.X) * (p2.Y- p1.Y) / (p2.X - p1.X); if (pt.Y> p.Y) {

nCross++; } } }

p1 = mwp[mwp.Count - 1]; p2 = mwp[0];

if (((p.X >= p1.X) && (p.X < p2.X)) || ((p.X >= p2.X) && (p.X < p1.X))) {

pt.Y = p1.Y + (p.X - p1.X) * (p2.Y - p1.Y) / (p2.X - p1.X); if (pt.Y > p.Y) {

nCross++; } }

if ((nCross > 0) && ((nCross % 2) == 1)) { returntrue; } else

{ returnfalse; } }

//射线法填充多边形

privatevoid FillPolygon(Color fillColor) {

int color= ColorTranslator.ToWin32(fillColor); Point minP = newPoint(); Point maxP = newPoint(); if (mwp.Count > 2)

PloygonEnvelope(ref minP, ref maxP); for (int y = minP.Y+1; y <= maxP.Y; y++) {

for (int x = minP.X+1; x <= maxP.X; x++) {

if(IsInPolygon(newPoint(x,y))) {

//g.FillRectangle(Brushes.Green, x, y, 1, 1); SetPixelColor(x, y,color); } } } }

//设置指定像素的颜色

privatevoid SetPixelColor(int x,int y,int color) {

SetPixel(g.GetHdc(), x, y, color); g.ReleaseHdc(); int i=Color.Green.ToArgb();

//int i = ColorTranslator.ToWin32(Color.Green);

//int i = ColorTranslator.ToWin32(GetPixelColor(x, y)); }

// 获取指定像素的颜色

privateuint GetPixelColor(int x,int y) {

uint pixel = GetPixel(g.GetHdc(),x,y); g.ReleaseHdc();

// Color color = Color.FromArgb((int)(pixel & 0x0000FF), //(int)(pixel & 0x00FF00) >> 8, //(int)(pixel & 0xFF0000) >> 16); return pixel; } //扫描线种子填充

privatevoid SeedFillPolygon(int seedx, int seedy, Color boundary_color, Color fill_color) {

int fillValue = ColorTranslator.ToWin32(fill_color);

int boundaryValue = ColorTranslator.ToWin32(boundary_color); List pointStack = newList();

int Savex, Xright, Xleft, Pflag, Xenter; int X, Y;

Point tmpPoint = newPoint(); tmpPoint.X = seedx; tmpPoint.Y = seedy; pointStack.Add(tmpPoint); while (pointStack.Count > 0) {

tmpPoint = pointStack[pointStack.Count - 1]; X = tmpPoint.X; Y = tmpPoint.Y;

pointStack.RemoveAt(pointStack.Count - 1); SetPixelColor(X, Y, fillValue); Savex = X; ++X;

while (GetPixelColor(X, Y) != boundaryValue) {

SetPixelColor(X, Y, fillValue); ++X; }

Xright = X - 1; X = Savex; X = X - 1;

while (GetPixelColor(X, Y) != boundaryValue) {

SetPixelColor(X, Y,fillValue); --X; }

Xleft = X + 1; X = Savex;

X = Xleft; ++Y;

while (X <= Xright) {

Pflag = 0;

while ((GetPixelColor(X, Y) != boundaryValue) &&

(GetPixelColor(X, Y) != fillValue) && (X < Xright)) {

if (0 == Pflag)

{

Pflag = 1; }

++X; }

if (Pflag == 1) { if ((X == Xright) &&

(GetPixelColor(X, Y) != boundaryValue) && (GetPixelColor(X, Y) != fillValue)) { Point point = newPoint(X, Y);

pointStack.Add(point); } else

{

Point point = newPoint(X - 1, Y);

pointStack.Add(point); }

Pflag = 0; }

Xenter = X;

while ((GetPixelColor(X, Y) == boundaryValue ||

GetPixelColor(X, Y) == fillValue) && X < Xright) { ++X; }

if (X == Xenter) { ++X; } }

X = Xleft; Y -= 2;

while (X <= Xright)

{

Pflag = 0;

while ((GetPixelColor(X, Y) != boundaryValue) &&

(GetPixelColor(X, Y) != fillValue) && (X < Xright)) { if (0 == Pflag)

{

Pflag = 1; }

++X; }

if (Pflag == 1) { if ((X == Xright) &&

(GetPixelColor(X, Y) != boundaryValue)&& (GetPixelColor(X, Y) != fillValue)) { Point point = newPoint(X, Y);

pointStack.Add(point); } else

{

Point point = newPoint(X - 1, Y);

pointStack.Add(point); }

Pflag = 0; }

Xenter = X;

while ((GetPixelColor(X, Y) == boundaryValue ||

GetPixelColor(X, Y) == fillValue) && X < Xright) { ++X; }

if (X == Xenter) { ++X;

} } } }

privatevoid btnSeedLine_Click(object sender, EventArgs e) {

if (mSeed.X==0 && mSeed.Y==0) {

MessageBox.Show(\请先选择一颗种子\, \提示\); return; } if(mwp.Count>2)

SeedFillPolygon(mSeed.X,mSeed.Y, Color.Black,btnFillColor.BackColor); }

privatevoid btnFillColor_Click(object sender, EventArgs e) {

if(colorDialog1.ShowDialog()==DialogResult.OK)

btnFillColor.BackColor = colorDialog1.Color; }

privatevoid btnGetSeed_Click(object sender, EventArgs e) {

Flag = 3;

picShow.Cursor = System.Windows.Forms.Cursors.Default; }

privateint fill= ColorTranslator.ToWin32(Color.Green); privateint edge = ColorTranslator.ToWin32(Color.Black); //8

privatevoid FillPolygonByColor(int x,int y) { uint k =2;

SetPixelColor(x,y, fill); for (int i = x - 1; i <= x + 1; i++) {

for (int j = y - 1; j <= y + 1; j++) {

k=GetPixelColor(i, j); if (k!= fill && k!= edge) {

FillPolygonByColor(i, j); } } }

}

privatevoid button1_Click(object sender, EventArgs e) {

FillPolygonByColor(mSeed.X, mSeed.Y); } } }

八、实验结果

实验三:曲线、曲面的生成

实验学时:4学时 实验类型:设计 实验要求:必修 一、实验目的

利用Bezier,B样条或NuBer(非均匀有理B样条)曲线,生成图2茶壶,以加深对曲线曲面生成三维图形的原理,控制方法以及算法的实现(包括隐藏线,面的消除),要求生成的茶壶要光滑。 二、实验内容

掌握曲线、曲面的表示方法,实现茶壶的三维造型设计。通过程序设计实现上述算法。建议采用VC++实现OpenGL程序设计。 三、实验原理、方法和手段

采用三次Bezier 曲线或其它函数曲线生成图2所示茶壶。

利用三次Bezier曲线生成时,n=3,若写成向量或写成 t 的三次参数式为:

??13?3?3?63t1???330?00?11??P0??P?0???1? t?[0,1]0??P2????0??P3?

p(t)?t3?t2?其中:P0,P1,P2,P3是特征多边形上四个顶点(控制点)的向量。若将其分解

??13?3?3?63t1???330?00?11??x0??x?0???1? t?[0,1]0??x2????0??x3?x(t)?t3?t2?为二维平面中x、y方向的分量,则

x(t)?A0?A1*t?A2*t2?A3*t3 y(t)?B0?B1*t?B2*t2?B3*t3

y(t)?t3?t2??13?3?3?63t1???330?00?1?1??y0??y?0???1? t?[0,1]0??y2????0??y3?A0?x0

A1??3*x0?3*x1 A2?3*x0?6*x1?3*x2 A3??x0?3*x1?3*x2?x3

B0?y0

B1??3*y0?3*y1 B2?3*y0?6*y1?3*y2 B3??y0?3*y1?3*y2?y3

图2 茶壶

以下分析以下茶壶的绘制方法:

可以假定壶体表面是一个旋转面,因此只要在x-y 平面上找到用来生成旋转面的基本曲线就可以了。采用分段三次贝齐尔曲线,选十个控制点,它们的数据见表1(a),控制点位置与生成的曲线见图3a。这条曲线由三段组成,分别由点[1,2,3,4],[4,5,6,7],[7,8,9,10]控制。注意到4,7公用,且3,4,5在一直线上,6,7,8也在一直线上,因而可知三段曲线拼接后有一阶连续性。类似地,壶盖也视为旋

转面,它的外轮廓由两段贝齐尔曲线生成,分别由[1,2,3,4]和[4,5,6,7]控制。由于3,4,5共线,4为公共点,故也有一阶连续性。这七个点的数据见表1(b),对应的轮廓线见图3b。旋转面的显示可给定拟合后外形线上的一点(x0,y0),绕y轴旋转得Y=Y0平面上一个圆。若只作沿Z轴平行投影变换,即只去掉z分量,则它显示成一线段。一个避免这种现象的简便方法是将它们绕X轴适当旋转同一角度,然后再投影到x-y平面上显示。

茶壶的壶嘴则要用贝齐尔曲面片来表示(也可以用曲线,但需插入控制点)。首先确定剖面的外轮廓线。这里有两条,每一条也由七个控制点控制,它们的数据见表1(c),外轮廓线见图3c,但是要注意现在要再增加二条线(每线也相应的有七个控制点),它们分别与两外轮廓线有相同的x、y坐标,只是z的坐标不再是零,而是有变化,这个变化的方式是:与11,12(21,22)相对应的=0.66:与13,14,15(23,24,25)对应的为z =0.25:与另二点对应的为z =0.15,这样壶嘴实际上由二片贝齐尔曲面片(每片十六个控制点)组成。例如其中一片上的四个控制点分别为(11x,11y,0),(11x,11y,0.66),(21x,21y,0.66),(21x,21y,0),其中11x,11y分别指第十一个控制点x,y坐标。对每一曲面片的显示,实际上可以显示八条曲线,或者适当加密。类似地处理壶的把手,唯一与壶嘴有别处是另外两线的z不随控制点不同而变,即取定值z =0.3。数据见表1(d)。外轮廓线图见3d,最后,壶底可以用半径为1.5的圆盘来表示,容易想象它应如何加入这一图形生成过程中。图2就是用这种方法画出的茶壶。

表1(a) 壶体控制点数据 表1(b) 壶盖控制点数据 1 2 3 4 5 6 7 8 9 x 1.4000 1.3375 1.4375 1.5000 1.7500 2.0000 2.0000 2.0000 1.5000 y 2.25000 2.38125 2.38125 2.25000 1.72500 1.20000 0.75000 0.30000 0.07500 1 2 3 4 5 6 7 x 0.0 0.8 0.0 0.2 0.4 1.3 1.3 y 3.0 3.0 2.7 2.55 2.4 2.4 2.25 10 1.5000 0.0000

表1(c) 壶嘴控制点数据 x (11) 1.7 (12) 2.6 (13) 2.3 (14) 2.7 (15) 2.8 (16) 2.9 (17) 2.8 (21) 1.7 (22) 3.1 (23) 2.4 (24) 3.3 (25) 3.525 (26) 3.4 (27) 3.2 表1(d) 壶嘴控制点数据 x (11) –1.6 (12) –2.3 (13) –2.7 (14) –2.7 (15) –2.7 (16) –2.5 (17) –2.0 (21) –1.5 (22) –2.5 (23) –3.0 (24) –3.0 (25) –3.0 (26) –2.65 (27) –1.90 (11) 1.875 (12) 1.875 (13) 1.875 (14) 1.65 (15) 1.425 (16) 0.975 (17) 0.75 y (21) 2.1 (22) 2.1 (23) 2.1 (24) 1.65 (25) 1.20 (26) 0.7875 (27) 0.45 (11) 1.27 (12) 1.275 (13) 1.95 (14) 2.25 (15) 2.325 (16) 2.325 (17) 2.25 y (21) 0.4 (22) 0.675 (23) 1.875 (24) 2.2 (25) 2.34 (26) 2.36 (27) 2.25

图3a 壶体外轮廓线 图3b 壶盖外轮廓线

图3c 壶嘴外轮廓线 图3d 壶把手外轮廓线

四、实验组织运行要求

本实验采用集中授课形式,每个同学独立完成上述实验要求。 五、实验条件

每人一台计算机独立完成实验。 六、实验步骤

(1) 壶体及壶盖的外轮廓线。 (2) 旋转生成壶体及壶盖。 (3) 完成壶嘴及把手的绘制。 七、程序代码

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。

//

#include\ #include #include #include

#pragmacomment(lib, \) usingnamespace std;

GLfloat roate = 0.0;// set rote of roate ying yu bu hao bu zhuang le 设置旋转速率 GLfloat rote = 0.0;//shezhi旋转角度 GLfloat anglex = 0.0;//X 轴旋转 GLfloat angley = 0.0;//Y 轴旋转 GLfloat anglez = 0.0;//Z 轴旋转 GLint WinW = 400; GLint WinH = 400;

GLfloat oldx;//当左键按下时记录鼠标坐标 GLfloat oldy; void init(void) {

glClearColor(1.0, 1.0, 1.0, 1.0); //背景黑色 }

void display(void) {

glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 0.0, 0.0); //画笔红色 glLoadIdentity(); //加载单位矩阵

gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glRotatef(rote, 0.0f, 1.0f, 0.0f); glRotatef(anglex,1.0,0.0,0.0); glRotatef(angley,0.0,1.0,0.0); glRotatef(anglez,0.0,0.0,1.0); glutWireTeapot(2); rote += roate;

//glRotatef(angle, 0.0, 1.0, 0.0); //angle += 1.0f; glutSwapBuffers(); }

void reshape(intw, inth) {

glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity();

gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); }

void mouse(intbutton, intstate, intx, inty) {

if (button == GLUT_LEFT_BUTTON) {

if (state == GLUT_DOWN) {

roate = 0; rote = 0;

oldx = x;//当左键按下时记录鼠标坐标 oldy = y;

cout <<\<< endl; } }

if (button == GLUT_RIGHT_BUTTON) {

if (state == GLUT_DOWN) {

roate += 1.0f;

cout <<\<< endl; } } }

void motion(intx, inty) {

GLint deltax = oldx - x; GLint deltay = oldy - y;

anglex += 360 * (GLfloat)deltax / (GLfloat)WinW;//根据屏幕上鼠标滑动的距离来设置旋转的角度

angley += 360 * (GLfloat)deltay / (GLfloat)WinH; anglez += 360 * (GLfloat)deltay / (GLfloat)WinH; oldx = x;//记录此时的鼠标坐标,更新鼠标坐标

oldy = y;//若是没有这两句语句,滑动是旋转会变得不可控 glutPostRedisplay(); glutPostRedisplay(); }

int main(intargc, char** argv) {

glutInit(&argc, argv);

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(600, 600); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init();

glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMotionFunc(motion); glutIdleFunc(display); glutMainLoop(); return 0; }

八、实验结果

本文来源:https://www.bwwdw.com/article/pzgr.html

Top