用C#实现计算机图形学算法
? ? 多數(shù)情況下計(jì)算機(jī)圖形學(xué)算法都用C++實(shí)現(xiàn),下面鄙人用C#實(shí)現(xiàn)一部分算法。并附上運(yùn)行截圖。
一 圖案
1 金剛石
? ? 金剛石圖案是每一個(gè)頂點(diǎn)都與其他頂點(diǎn)相連的正n邊形。金剛石圖案有時(shí)被用作計(jì)算機(jī)圖形設(shè)備的測試圖案。通過觀察交匯于每個(gè)頂點(diǎn)的直線所呈現(xiàn)出來的擁擠和模糊程度,可以確定設(shè)備的分辨率。
? ? 做程序時(shí),使用線段連接每個(gè)頂點(diǎn)時(shí)不進(jìn)行重復(fù)連接。
? ? 主要代碼如下:
? ? ? ? ? ? //n為等分點(diǎn)的個(gè)數(shù),r為圓的半徑
? ? ? ? ? ? Pen p = new Pen(Color.Blue, 1);//定義了一個(gè)藍(lán)色,寬度為的畫筆
? ? ? ? ? ? double Thta;//thta為圓的等分角
? ? ? ? ? ? Thta = 2 * PI / jgsn;
? ? ? ? ? ? for (int i = 0; i < jgsn; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? pt[i].X = (int)(r * Math.Cos(i * Thta) + maxX / 2);
? ? ? ? ? ? ? ? pt[i].Y = (int)(r * Math.Sin(i * Thta) + maxY / 2);
? ? ? ? ? ? }
? ? ? ? ? ? for (int i = 0; i <= jgsn - 2; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int j = i + 1; j <= jgsn - 1; j++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? //dc.MoveTo(ROUND(pt3[i].x),ROUND(pt3[i].y));
? ? ? ? ? ? ? ? ? ? //g.DrawLine(p,pt[i],pt[j]);
? ? ? ? ? ? ? ? ? ? g.DrawLine(p, pt[i].X, pt[i].Y, pt[j].X, pt[j].Y);
? ? ? ? ? ? ? ? ? ? //dc.LineTo(ROUND(pt3[j].x),ROUND(pt3[j].y));
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
?
運(yùn)行結(jié)果如下:
?
? ? 金剛石圖案按孔令德老師所說,等分點(diǎn)數(shù)為奇數(shù)時(shí)圖案中心為一個(gè)圓,等分點(diǎn)數(shù)為偶數(shù)時(shí)圖案中心為一個(gè)點(diǎn),運(yùn)行結(jié)果確實(shí)如此。
2 Menger海綿
? ? 將一個(gè)立方體沿其各個(gè)面三等分為27個(gè)小立方體,舍棄位于體心的一個(gè)小立方體,以及位于立方體六個(gè)面心處的6個(gè)小立方體。將剩余的20個(gè)小立方體繼續(xù)按相同的方法分割與舍棄,就能得到中間有大量空隙的Menger海綿。
? ? 算法步驟:
? ? 輸入遞歸深度n;
? ? 繪制初始立方體;
? ? 將立方體的每條邊三等分,舍棄6個(gè)位于面心的小立方體和一個(gè)位于體心處的小立方體,使用畫家算法繪制余下的20個(gè)小立方體;
? ? 執(zhí)行遞歸子程序,對其余20個(gè)小立方體進(jìn)行遞歸,直到n為0。
?
? ? 具體代碼就不貼出了,有興趣可以自己下載了查看;運(yùn)行效果如下;
?
? ? 繪圖時(shí)可以用C#的Point類型的數(shù)組表示點(diǎn)集,如果用C++的話通常需要自己定義一個(gè)二維點(diǎn)類。在編寫圖形學(xué)算法代碼時(shí),還會(huì)用到三維點(diǎn)類,這就需要自己定義了。
? ? 另外C#的二維數(shù)組定義與C++頗有不同,其定義類似如下;
?private int[,] TM=new int[4,4];//變換矩陣
? ? 二維數(shù)組作函數(shù)參數(shù)的寫法則類似如下,與C++不同;
private void KeepOriginalMatrix(int[,] Orig,int[,] Dest)
?
二 基本圖元算法
1 圓中點(diǎn)Bresenham算法
? ??Bresenham算法是計(jì)算機(jī)圖形學(xué)領(lǐng)域使用廣泛的直線掃描轉(zhuǎn)換方法。其原理是:過各行、各列像素中心構(gòu)造一組虛擬網(wǎng)格線,按直線從起點(diǎn)到終點(diǎn)的順序計(jì)算直線各垂直網(wǎng)格線的交點(diǎn),然后確定該列像素中與此交點(diǎn)最近的像素。
? ? 此處為了清楚演示計(jì)算機(jī)圖形學(xué)的原理,并不畫計(jì)算出的每一個(gè)點(diǎn),而是先生成Button的控件數(shù)組,在每個(gè)點(diǎn)處放一個(gè)小Button,Button的長和寬為10;這樣出來的效果相當(dāng)于把屏幕單個(gè)像素放大了10倍;如下圖所示;
? ? 設(shè)置Button數(shù)組坐標(biāo)的代碼如下,其他部分可自行下載查看;
private void CirclePoint(int x, int y,int cnt)//八分法畫圓子函數(shù)
? ? ? ? {
? ? ??
? ? ? ? ? ? btny[0 + 8 * cnt].Left = x + maxX / 2 - 8;
? ? ? ? ? ? btny[0 + 8 * cnt].Top = y + maxY / 2 - 8;
? ? ? ? ? ? btny[1 + 8 * cnt].Left = y + maxX / 2 - 8;
? ? ? ? ? ? btny[1 + 8 * cnt].Top = x + maxY / 2 - 8;
? ? ? ? ? ? btny[2 + 8 * cnt].Left = y + maxX / 2 - 8;
? ? ? ? ? ? btny[2 + 8 * cnt].Top = -x + maxY / 2 - 8;
? ? ? ? ? ? btny[3 + 8 * cnt].Left = x + maxX / 2 - 8;
? ? ? ? ? ? btny[3 + 8 * cnt].Top = -y + maxY / 2 - 8;
? ? ? ? ? ? btny[4 + 8 * cnt].Left = -x + maxX / 2 - 8;
? ? ? ? ? ? btny[4 + 8 * cnt].Top = -y + maxY / 2 - 8;
? ? ? ? ? ? btny[5 + 8 * cnt].Left = -y + maxX / 2 - 8;
? ? ? ? ? ? btny[5 + 8 * cnt].Top = -x + maxY / 2 - 8;
? ? ? ? ? ? btny[6 + 8 * cnt].Left = -y + maxX / 2 - 8;
? ? ? ? ? ? btny[6 + 8 * cnt].Top = x + maxY / 2 - 8;
? ? ? ? ? ? btny[7 + 8 * cnt].Left = -x + maxX / 2 - 8;
? ? ? ? ? ? btny[7 + 8 * cnt].Top = y + maxY / 2 - 8;
? ? ? ? }
?
三 二維變換
1 首先繪制坐標(biāo)系和基本圖形
?
2 二維變換是通過矩陣運(yùn)算完成的
? ? 相關(guān)代碼如下;
private void DrawShape(Graphics g) //畫四邊形
? ? ? ? {
? ? ? ? ? ??
? ? ? ? ? ? KeepOriginalMatrix(P24, OSquare);
? ? ? ? ? ? Pen pen = new Pen(Color.Green, 2);
? ? ? ? ? ? g.DrawLine(pen, maxX / 2 + P24[0, 0], maxY / 2 - P24[0, 1], maxX / 2 + P24[1, 0], maxY / 2 - P24[1, 1]);
? ? ? ? ? ? g.DrawLine(pen, maxX / 2 + P24[1, 0], maxY / 2 - P24[1, 1], maxX / 2 + P24[2, 0], maxY / 2 - P24[2, 1]);
? ? ? ? ? ? g.DrawLine(pen, maxX / 2 + P24[2, 0], maxY / 2 - P24[2, 1], maxX / 2 + P24[3, 0], maxY / 2 - P24[3, 1]);
? ? ? ? ? ? g.DrawLine(pen, maxX / 2 + P24[3, 0], maxY / 2 - P24[3, 1], maxX / 2 + P24[0, 0], maxY / 2 - P24[0, 1]);
? ? ? ? }
? ? ? ? private void KeepOriginalMatrix(int[,] Orig,int[,] Dest)//保留矩陣
? ? ? ? {
? ? ? ?int i,j;
? ? ? ?for(i=0;i<4;i++)
? ? ? ?for(j=0;j<3;j++)
? ? ? ?Dest[i,j]=Orig[i,j];
? ? ? ? }
? ? ? ? private void Tmove(int Tx,int Ty)//平移變換矩陣
? ? ? ? {
? ? ? ?ClearMatrix(TM);
? ? ? ?TM[0,0]=1;
? ? ? ?TM[1,1]=1;
? ? ? ?TM[2,0]=Tx;
? ? ? ?TM[2,1]=Ty;
? ? ? ?TM[2,2]=1;
? ? ? ?Calculate(P24,TM);
? ? ? ? ? ??
? ? ? ? }
? ? ? ? private void Trotate(double thta)//旋轉(zhuǎn)變換矩陣
? ? ? ? {
? ? ? ?ClearMatrix(TR2);
? ? ? ?TR2[0,0]=(int) Math.Cos(thta*PI/180);
? ? ? ? ? ? TR2[0, 1] = (int)Math.Sin(thta * PI / 180);
? ? ? ? ? ? TR2[1, 0] = (int) -Math.Sin(thta * PI / 180);
? ? ? ? ? ? TR2[1, 1] = (int)Math.Cos(thta * PI / 180);
? ? ? ?TR2[2,2]=1;
? ? ? ?Calculate(P24,TR2);
? ? ? ?
? ? ? ? }
? ? ? ? private void Tscale(double Sx,double Sy)//比例變換矩陣
? ? ? ? {
? ? ? ?ClearMatrix(TS2);
? ? ? ?TS2[0,0]=(int)Sx;
? ? ? ?TS2[1,1]=(int)Sy;
? ? ? ?TS2[2,2]=1;
? ? ? ?Calculate(P24,TS2);
? ? ? ?
? ? ? ? }
? ? ? ? private void Treform(double b,double c)//錯(cuò)切變換矩陣
? ? ? ? {
? ? ? ?ClearMatrix(TC2);
? ? ? ?TC2[0,0]=1;
? ? ? ?TC2[0,1]=(int)b;
? ? ? ?TC2[1,0]=(int)c;
? ? ? ? ? ? TC2[1,1]=1;
? ? ? ?TC2[2,2]=1;
? ? ? ?Calculate(P24,TC2);
? ? ? ?
? ? ? ? }
? ? ? ? private void ClearMatrix(int[,] A)//清除變換矩陣
? ? ? ? {
? ? ? ?for(int i=0;i<3;i++)
? ? ? ?{
? ? ? ?for(int j=0;j<3;j++)
? ? ? ?A[i,j]=0;
? ? ? ?}
? ? ? ? }
? ? ? ? private void Calculate(int[,] P0,int[,] T)//兩個(gè)矩陣相乘
? ? ? ? {
? ? ? ?int[,] Ptemp=new int[4,3];
? ? ? ?KeepOriginalMatrix(P24,Ptemp);
? ? ? ? ? ? for (int i = 0; i < 4; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int j = 0; j < 3; j++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? P24[i, j] = Ptemp[i, 0] * T[0, j] + Ptemp[i, 1] * T[1, j] + Ptemp[i, 2] * T[2, j];
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
?
下面是平移,縮放,錯(cuò)切的運(yùn)行結(jié)果;從菜單中畫初始圖形;用四個(gè)方向鍵實(shí)現(xiàn)平移;用M鍵實(shí)現(xiàn)放大;用C和D鍵實(shí)現(xiàn)2個(gè)方向的錯(cuò)切;
?
?
?
四 基本三維
1 畫一個(gè)三維立方體
? ? 主要代碼如下;
private void DrawCube(Graphics g)//繪制立方體
? ? ? ? {
? ? ? ?Point[] p=new Point[4];//定義多邊形頂點(diǎn)數(shù)組
? ? ? ?Transform3DTo2D(P3D,P2D,8);
? ? ? ?//繪制立方體左面
? ? ? ?p[0]=new Point(maxX/2+P2D[0,0],(maxY/2+P2D[0,1]));
? ? ? ?p[1]=new Point(maxX/2+P2D[3,0],(maxY/2+P2D[3,1]));
? ? ? ?p[2]=new Point(maxX/2+P2D[7,0],(maxY/2+P2D[7,1]));
? ? ? ?p[3]=new Point(maxX/2+P2D[4,0],(maxY/2+P2D[4,1]));
? ? ? ? ? ? Pen ?pen = new ?Pen(Color.Blue, 2);
? ? ? ? ? ? g.DrawLines(pen, p);
? ? ? ? ? ? //繪制立方體后面
? ? ? ?p[0]=new Point(maxX/2+P2D[0,0],(maxY/2+P2D[0,1]));
? ? ? ?p[1]=new Point(maxX/2+P2D[1,0],(maxY/2+P2D[1,1]));
? ? ? ?p[2]=new Point(maxX/2+P2D[2,0],(maxY/2+P2D[2,1]));
? ? ? ?p[3]=new Point(maxX/2+P2D[3,0],(maxY/2+P2D[3,1]));
? ? ? ? ? ? g.DrawLines(pen, p);
? ? ? ?//繪制立方體底面
? ? ? ?p[0]=new Point(maxX/2+P2D[0,0],(maxY/2+P2D[0,1]));
? ? ? ?p[1]=new Point(maxX/2+P2D[4,0],(maxY/2+P2D[4,1]));
? ? ? ?p[2]=new Point(maxX/2+P2D[5,0],(maxY/2+P2D[5,1]));
? ? ? ? ? ? p[3]=new Point(maxX/2+P2D[1,0],(maxY/2+P2D[1,1]));
? ? ? ? ? ? g.DrawLines(pen, p);
? ? ? ?//繪制立方體右面
? ? ? ?p[0]=new Point(maxX/2+P2D[1,0],(maxY/2+P2D[1,1]));
? ? ? ?p[1]=new Point(maxX/2+P2D[2,0],(maxY/2+P2D[2,1]));
? ? ? ?p[2]=new Point(maxX/2+P2D[6,0],(maxY/2+P2D[6,1]));
? ? ? ?p[3]=new Point(maxX/2+P2D[5,0],(maxY/2+P2D[5,1]));
? ? ? ? ? ? g.DrawLines(pen, p);
? ? ? ?//繪制立方體頂面
? ? ? ?p[0]=new Point(maxX/2+P2D[3,0],(maxY/2+P2D[3,1]));
? ? ? ?p[1]=new Point(maxX/2+P2D[7,0],(maxY/2+P2D[7,1]));
? ? ? ?p[2]=new Point(maxX/2+P2D[6,0],(maxY/2+P2D[6,1]));
? ? ? ?p[3]=new Point(maxX/2+P2D[2,0],(maxY/2+P2D[2,1]));
? ? ? ? ? ? g.DrawLines(pen, p);
? ? ? ?//繪制立方體前面
? ? ? ?p[0]=new Point(maxX/2+P2D[4,0],(maxY/2+P2D[4,1]));
? ? ? ?p[1]=new Point(maxX/2+P2D[5,0],(maxY/2+P2D[5,1]));
? ? ? ?p[2]=new Point(maxX/2+P2D[6,0],(maxY/2+P2D[6,1]));
? ? ? ?p[3]=new Point(maxX/2+P2D[7,0],(maxY/2+P2D[7,1]));
? ? ? ? ? ? g.DrawLines(pen, p);
? ? ? ? }
效果如下;
?
代碼和可執(zhí)行文件下載:
http://pan.baidu.com/s/1hqF0Jic
?
總結(jié)
以上是生活随笔為你收集整理的用C#实现计算机图形学算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图解MySQL删除再安装教程
- 下一篇: C# 类和编程案例