Các thuật toán trong đồ họa máy tính: Thuật toán vẽ đường tròn

Phương trình đường tròn có dạng: (x-xc)2 + (y-yc)2 = r2 Pt đường tròn có tâm ở gốc tọa độ: x2+y2 =r2 Do tính đối xứng của đường tròn nên ta chỉ cần vẽ cung ¼ hoặc 1/8 void put8pixel(int xc, int yc, int x, int y) {

doc21 trang | Chia sẻ: haohao89 | Lượt xem: 4845 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Các thuật toán trong đồ họa máy tính: Thuật toán vẽ đường tròn, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
I.Thuật toán vẽ đường tròn Phương trình đường tròn có dạng: (x-xc)2 + (y-yc)2 = r2 Pt đường tròn có tâm ở gốc tọa độ: x2+y2 =r2 Do tính đối xứng của đường tròn nên ta chỉ cần vẽ cung ¼ hoặc 1/8 void put8pixel(int xc, int yc, int x, int y) { putpixel(x+xc, y+yc, color); putpixel(y+xc, x+yc, color); putpixel(y+xc, -x+yc, color); putpixel(x+xc, -y+yc, color); putpixel(-x+xc, -y+yc, color); putpixel(-y+xc, -x+yc, color); putpixel(-y+xc, x+yc, color); putpixel(-x+xc, y+yc, color); } 1.Thuật toán Bresenham Giả sử tại bước i đã vẽ được điểm (xi,yi) Điểm cần vẽ kế tiếp (xi+1, yi+1) là: (xi +1, yi) hay (xi +1, yi -1) Giá trị y thực sự thuộc đường tròn ứng với xi là: y2 = r2 – (xi +1)2 Gọi d1 = yi2 – y2 = yi2 –r2 +(xi +1)2 d2= y2 - (yi -1)2 = r2 – (xi +1)2 – (yi -1)2 Pi = d1-d2 = yi2 – r2 +(xi +1)2 –r2 + (xi +1)2 +(yi-1)2 = 2(xi +1)2 + yi2 +(yi -1)2 -2r2 Pi+1 – pi = 2(xi+1 +1)2 + yi+12 + (yi+1 -1)2 – 2r2 – 2(xi +1)2 – yi2 – (yi -1)2 + 2r2 = 4xi + 6 + 2(yi+12 – yi2) -2(yi+1-yi) Pi+1 = pi+4xi + 6 + 2(yi+12 – yi2) -2(yi+1-yi) Vậy: + nếu pi < 0 thì yi+1 = yi, khi đó pi+1 = pi+4xi+6 +Nếu pi >= thì yi+1 = yi-1, khi đó pi+1 = pi + 4(xi-yi)+10 Giá trị pi tại điểm đầu tiên (x1,y1) = (0,r) là: P1 = 2+r2+(r-1)2 – 2r2=3-2r void CircleBres(int xc, int yc, int r) { int x,y,p; x=0; y=r; p=3-2*r; while (x<=y) { put8pixel(xc,yc,x,y); if(p<0) p+ =4*x+6; else { p+ = 4*(x-y) +10; y--; } x++; }} 2.Thuật toán Midpoint Gọi F(x,y) = x2+y2-r2, ta có: F(x,y){<0 nếu (x,y) nằm trong đường tròn =0 nếu (x,y) thuộc đường tròn >0 nếu (x,y) nằm ngoài đường tròn Chọn điểm bắt đầu vẽ là (0,r) Giả sử đã vẽ được điểm (xi,yi), Điểm cần vẽ kế tiếp (xi+1,yi+1) là S hay P Việc chọn điểm S hau P dựa trên dấu của: Pi=F(Midpoint) = F(xi+1,yi-1/2) + Nếu pi chọn S + Nếu pi>=0 => chọn P Mặt khác: pi+1 – pi = F(xi+1 +1, yi+1 -1/2)- F(xi+1, yi-1/2) = [(xi+1 +1)2 + (yi+1 – ½)2 – r2] – [(xi+1)2 + (yi-1/2)2 – r2] = 2xi + 3 (yi+12 – yi2) – (yi+1 – yi) Vậy: + Nếu pi<0 thì yi+1 = yi, khi đó pi+1 = pi + 2xi + 3 + Nếu pi>=0 thì yi+1= yi -1, khi đó pi+1 = pi + 2(xi-yi) +5 Giá trị pi tại điểm đầu tiên (x1, y1)=(0,r) là: p1= F(x1+1, y1 -1/2) = f(1, r-1/2) = 5/4 – r void CircleMidpoint(int xc, int yc, int r) { int x,y,p; x=0; y=r; p=5/4; while (x<=y0 { put8pixel(xc,yc,x,y); if(p<0) p+ = 2*x +3; else{ p+=2*(x-y0+5; y--; } x++; } } II.Thuật toán tô màu theo đường biên: Ý tưởng: Bắt đầu từ điểm P(x,y) nằm bên trong vùng tô, kiểm tra các điểm lân cận cảu P đã được tô màu hay có phải là điểm biên hay không, nếu không phải là điểm đã tô và không phỉa là điểm biên thì ta sẽ tô màu nó. Quá trình này được lặp laijcho đến khi không còn tô được diểm nào nữa thì dừng. Có hai cách chọn điểm lan cận, đó là chọn 4 hoặc 8 điểm lân cận dối với điểm đang xét. Thủ tục minh hoajthuaatj toán tô màu theo đường biên: Void TOloang(int x, int y, int mauto, int mauvien) { int mau; mau=getpixel(x,y); if(mau != mauvien)& (mau != mauto) { Putpixel(x, y,mauto); Toloang(x-1,y, mauto, mauvien); Toloang(x, y+1, mauto, mauvien); Toloang(x+1, y, mauto, mauvien); Toloang(x, y-1, mauto, mauvien);}} =>Nhược điểm của phương pháp đệ quy là =>không thực hiện được khi vùng loang có diện =>tích lớn( dẫn đến tràn Stack 1.Phương pháp đệ quy: Bước 1: khởi tạo hang đợi (hoặc stack) với phần tử đầu tiên là P(x,y) đã được tô Bước 2: khi hàng đợi (hoặc stack) không rỗng thì: + Lấy ra từ hang đợi(hoặc stack) một điểm Q. + tìm các điểm lân cận của Q chưa tô thì tô chúng và đưa chúng vào hang đợi(hoặc stack) Bước 3 này được lặp đi lặp lại cho đến khi hang đợi (hoặc stack) rỗng struct DS { int x,y; struct DS*next; }; struct DS*dau; void push(int x, inty) { struct DS*P; P=(DS*) calloc(1,sizeof(DS)); P->x=x; P->y=; P->next=NULL; if(dau !=NULl) P->next= dau; dau=P;} Void pop(int *x, int*y) { struct DS*P; P=dau; dau=dau->next; *x=P->x; *y= P->y; free(P);} void tolancan(int x, inty, int mauto, int mauvien) { int mau; mau=getpixel(x,y); if((mau != mauto)&&(mau !=mauvien)) { putixel(x,y,mauto); push(x,y);} } Void toloang_stack(int x0, int y0, int mauto, int mauvien){ int x,y; putpixel(x0,y0,mauto); dau=NULL; push(xo,yo); while(dau != NULL) { pop(&x,&y); tolancan(x-1, y, mauto, mauvien); tolancan(x+1, y, mauto, mauvien); tolancan(x, y+1, mauto, mauvien); tolancan(x, y-1, mauto, mauvien);}} Flood Fill/boundary fill Scan line fill/ scan conversion Đơn giản Phức tạp hơn Thuật toán rời rạc hóa trong không gian màn hình Thuật toán rời rạc hóa trong đối tượng hoặc/và không gian màn hình Yêu cầu gọi hệ thống GetPixel/Val Độc lập với thiêt bị Đòi hỏi điểm seed Không đòi hỏi điểm seed Yêu cầu stack rất lớn Yêu cầu stack nhỏ III.Các thuật toán xén hình 1.Thuật toán Cohen-sutherland Chia mặt phẳng thành 9 vùng: cửa sổ và 8 vùng xung quanh nó. Mỗi vùng được gán bởi một mã nhị phân 4 bit Giả sử có điểm P(x,y), lúc đó gán mã cho điểm P: Pleft={ 1, nếu Px < xmin Pright={ 1, nếu Px>xmax 0, ngược lại 0, ngược lại Pbottom={ 1, nếu Py<ymin Ptop= { 1, nếu Py>ymax 0, ngược lại 0, ngược lại Hàm tính mã int ma(point M){ int m=0; if(M.x<min.x) ml=1; if(M.x>max.x) ml=2; if(M.y<min.y) ml=4; if(M.y>max.y) ml=8; return m;} Xét đoạn thẳng AB, ta có các trường hợp sau: 1.Nếu (Ma(A)=0000) và (Ma(B)=000) {hay(Ma(A) or Ma(B)=0000} thì =>ClipD(F)=AB 2.Nếu (Ma(A) and Ma(B))≠0000) thì => ClipD(F) = Ø 3.Nếu ((Ma(A) and Ma(B))=0000) và (Ma(A)≠0000 hoặc Ma(B)≠0000) thì Giả sử Ma(A)≠0000 { nếu ma(A)=0 ta đổi vai trò A và B} -Nếu Ax=Bx(AB thẳng đứng ) thì +Nếu Ay>ymax (A ở trên) thì Ay=ymax’ ngược lại (A ở dưới) Ay=ymin => ClipD(F)=AB -Ngược lại (trường hợp Ax≠Bx): +Tính hệ số góc m=(By-Ay)/(Bx-Ax) {để tính giao của AB với HCN} Vì A nằm ngoài hình chữ nhật nên: +Nếu Ax<xmin thì thay A bởi điểm giao của AB với cạnh trái(nối dài) của HCN +Nếu Ax>xmax thì thay A bởi điểm giao của Ab với cạnh phải(nối dài) cua HCN +Nếu Ay<ymin thì thay A bởi điểm giao của AB với cạnh dưới(nối dài) của HCN +Nếu Ay>ymax thì thay A bởi giao điểm của Ab với cạnh trên(nối dài) của HCN Quá trình lặp lại này dừng khi xảy ra một trong hai trường hợp 1hay 2 void ClipCohen(Point A, oint B, Point wmin, Point wmax) { int thoat,ve; double m; thoat=0; ve=1; while(thoat==0) { if((ma(A) | ma(B)==0) thoat=1; else if((ma(A)&ma(B))!=0) {thoat=1; ve=0} else { if(m(A)==0) hoanvi(&A,&B); if(A.x==B.x) { if(A.y>wmax.y) A.y=wmax.y; else A.y=wmin.y;} else{ m=(double)(B.y-A.y)/(b.x-A.x); if(A.x<wmin.x){ A.y=A.y+(wmin.x-A.x)*m; A,x=wmin.x; } Else if(A.x>wmax.x){ A.y=A.y+(wmax.x-A.x)*m; A.x=wmax.x; } else if(A.y<wmin.y){A.x= A.x+(wmin.y-A.y)/m; A.y=wmin.y; } else if(A.y>wmax.y) { A.x=A.x+(wmax.y-A.y)/m; A.y=wmax.y; }}}}//end while if (ve) veduongthang(A,B);} 2.Thuật toán chia nhị phân Chia mặt phẳng thành 9 vùng, mỗi vùng được gán bởi một mã nhị phân 4 bit -Ý tưởng của thuật toán như sau: Lấy trung điểm của đoạn thẳng và kiểm tra mã của nó để loại dần các đoạn con không chứa giao điểm, và cuối cùng cho điểm giữa hội tụ về giao điểm của đoạn thẳng với hình chữ nhật, kết quả là ta thu được đoạn con nằm trong hình chữ nhật(nếu có) -Mệnh đề : Cho M trung điểm của đoạn AB, Mã(A)≠0000, Mã(B)≠0000, Mã(M)≠0000 Thì ta có: [mã(A) AND Mã(M)]≠0000 hoặc[Mã(M) AND Mã(B)]≠0000. -Ý nghĩa hình học của mệnh đề: Nếu cả ba điểm A, B, M đều ở ngoài hình chữ nhật thì có ít nhất một đoạn hoàn toàn nằm ngoài hình chữ nhật. -Thuật toán: 1.Nếu (Mã(A) = 0000) và Mã(B)=0000) thì =>ClipD(F) = AB 2.Nếu (Mã(A) AND Mã(B))≠0000 thì =>ClipD(F)=Ø 3.Nếu (Mã(A)≠0000) và Mã(B) = 0000) thì: đỏi vai trò của A, B và áp dụng 4 4.Nếu (Mã(A) = 0000) và (Mã(B) ≠0000) thì: P:=A; Q:=B; Trong khi |xP – xQ| + |yP – yQ|>=2 thì: Lấy trung điểm M của PQ Nếu Mã(M)≠0000 thì Q:=M. Ngược lại: P:=M. =>ClipD(F) = AP 5.Nếu (Mã(A) ≠ 0000 ≠ Mã(B)) và [Mã(A) AND Mã(B)]= 0000 thì: P:=A; Q:=B; Lấy M: trung điểm PQ; Khi (Mã(M)≠0000) và (|xP-xQ| + |yP-yQ|>=2) thì: Nếu (Mã(M) AND Mã(Q))≠0000 thì Q:=M; Ngược lại: P:M; Lấy M: trung điểm PQ Nếu Mã(M)≠0000 thì ClipD(F)=Ø Ngược lại, áp dụng 4 ta có: ClipD(MP) = MA1 ClipD(MQ) = MB1 =>ClipD(F) = A1B1 Void XenHinhNhiPhan(Point A, Point B) { Point P,Q,M; if((Ma(A)|Ma(B))==0) VeDuongThang(A,B); if((Ma(A)&Ma(B))!=0) return; if((Ma(A)!=0)&&(Ma(B)=0)) HoanVi(&A,&B); if((Ma(A)==0)&&(Ma(B)!=0)) { P=A; Q=B; while((abs(P.x-Q.x)+abs(P.y-Q.y)).2) { M.x=(P.x+Q.x)/2; M.y=(P.y+Q.y)/2; If(Ma(M)==0) P=M; Else Q=M;} VeDuongThang(A,P);} if(((Ma(A)!=0)&&(Ma(B)!=0))&&((Ma(A)&Ma(B))==0)) { P=A; Q=B; M.x=(P.x+Q.x)/2; M.y=(P.y+Q.y)/2; while((Ma(M)!=0)&&((abs(P.x-Q.x)+abs(P.y-Q.y))>2)) { if((Ma(P)& Ma(M)!=0) P=M; else Q=M; M.x=(P.x+Q.x)/2; M.y=(P.y+Q.y)/2;} if(Ma(M)==0){ XenHinhNhiPhan(P,M); XenHihNhiPhan(M,Q);} IV.Biểu diễn các đối tượng 3D 1.Mô hình khung kết nối Mô hình khung kết nối (WF-WireFrame model) thể hiện hình dáng của một đối tượng 3D bởi 2 danh sách: -Danh sách các đỉnh (vertices): lưu tọa đọ các đỉnh. -Danh sách các cạnh (edges) nối gữa các đỉnh đó: lưu 2 đỉnh đầu và cuối của từng cạnh. (hình vẽ) Có nhiều cách để lưu trữ một mo hình WF trên máy tính. Có thể sử dụng cấu trúc sau: const maxDinh= 30; const maxCanh=50; typedef struct toado3D { int x; int y; int z; } typedef struct KieuCanh { int dau; int cuoi; } typedef struct WireFrame { int sodinh; toado3D dinh[maxDinh]; int socanh; KieuCanh canh[maxCnah]; }; 2.Mô hình mặt đa giác Mô hình các mặt đa giác(Polygon Mesh model) thể hiện hình dáng của một đối tượng 3D bởi 2 danh sách: -Danh sách các đỉnh -Danh sách các mặt: lưu thứ tự các đỉnh tạo nên mặt đó (hình vẽ) Chúng ta có thể đưa ra nhiều cấu trúc dữ liệu khác nhau để lưu trữ cho đa giác. Dưới đây là một kiểu cấu trúc: typedef struct toado3D { int x; int y; int z; } struct Kieumat { int sodinhmat; int chisodinh[MAX]; }; struct Mohinhmat { int sodinh; toado3D dinh[MAX]; int somat; Kieumat mat[MAX]; }; V.Biểu diễn đường và mặt cong: 1.Đường cong Benzier Bài toán: Cho n+1 điểm p0, p1, p2,…,pn được gọi là các điểm kiểm soát (điểm điều khiển). Xây dựng đường cong trơn đi qua 2 điểm p và pn được giới hạn trong bao lồi do n+1 điểm trên tạo ra Thuật toán Casteljau Để xây dựng đường cong P(t), ta dựa trên một dãy các điểm cho trước rồi tạo ra giá trị P(t) ứng với mỗi giá trị t nào đó. Phương pháp này tạo ra đường congdựa trên một dãy các bước nội suy tuyến tính hay nội suy khoảng giữa(In-Betweening). Với 3 điểm P0, P1, P2 ta có thể xây dựng một Parabol nội suy từ 3 điểm này bằng cách chọn một giá trị t ∊ [0,1] rồi chia đoạn P0P1 theo tỉ lệ t, ta được điểm P01, trên P0P1. Tương tự , ta chia tiếp P1P2 cũng theo tỉ lệ t, ta được P11. Nối P01 và P11 lại lấy điểm trên P01P11 chia theo tỉ lệ t, ta được P02. Tập điểm P02 chính là đường cong P(t). Ta biểu diễn bằng chương trình: P01(t)= (1-t).P0+t.P1 (1) P11(t)= (1-t).P1+t.P2 (2) P02(t)= (1-t).P01+t.P11 (3) Trong đó t ∊ [0. 1] Thay (1), (2) vào (3) ta được: P(t)= P02(t)= (1-t)2.P0 + 2t.(1-t).P1 + t2.p2 Đây là một đường cong bậc 2 theo t nên nó là một Parabol -Tổng quát hóa ta có thuật toán Casteljau cho (n+1) điểm kiểm soát: Giả sử ta có tập điểm: P0,P1,P2,…,Pn Với mỗi giá trị t cho trước, ta tạo ra điểm Pir(t) ở thế ệ thứ r, từ thế hệ thứ (r-1) trước đó, ta có: Pir(t)= (1-t).Pir-1(t)+t.Pi+1r-1(t) (r=0,1,…,n và i=0,…,n-r) Thế hệ cuối cùng: P0n(t)=∑ Pi.(1-t)n-i.ti.Cni Được gọi là dường cong benzier của các điểm P0,P1,P2,…,Pn. Các điểm pi, i=0,1,…,n gọi là các điểm kiểm soát hay các điểm beier. Đa gics tạo bởi các điểm kiểm soát gọi là đa giác kiểm soát hay đa giác benzier. Đường cong benzier dựa trên (n+1) điểm kiểm soát P0,P1,P2,…,Pn được cho bưởi công thức: P0n ( t)=P(t)= ∑pk.B nk(t) Trong đó: P(t): là một điểm trong mặt phẳng hoặc trong không gian. B nk(t): gọi là đa thức Bernstrin, được cho bởi coog thức: B nk(t)=Cnk .(1-t)n-k.tk =(n!/k!(n-k)! )(1-t)n-t.tk với n>=k Mỗi đa thức Bernstein có bậc là n. thong thuowgf ta còn gọi các B nk(t) là các hàm trộn. Đường cong Benzier dực trên (n+1) điểm kiểm soát p0,p1,…,pn được cho bưởi coog thức: P0n ( t)= P(t)= ∑Pk.B nk(t) Tương tự, đối với mặt benzier ta có phương trình sau: P(u,v)= ∑ ∑ P I,k . Bim (u) Bkn(V) Trong trường hợp này khối đa diện kiểm soát sẽ có (m+1)(n+1) đỉnh. Minh họa vẽ đường cong benzier trong mặt phẳng. Typedef struct { int x; int y; } CPoint; int GiaiThua (int n){ if(n==0) return 1 ; else return 1 n* GiaiThua(n-1); } float hamMu(float a, int n){ if(n==0) return 1; else return a*hamMu(a,n-1); } float BernStein(float t,int n, int k){ float ckn,kq; ckn=GiaiThua(n)/(GiaiThua(k)*GiaiThua(n-k)); kq=ckn*HamMu(1-t,n-k)*HamMu(t,k); return kq;} CPoint TPt(CPonit P[ ],float t,int n) {CPoint Pt; float B;int K; Pt.x=0; Pt.y=0; For(k=0;k<=n;k++) { B= BernStein(t,n,k); Pt.x=Pt.x+P[k].x*B; Pt.y=Pt.y+P[k].y*B; } return Pt; } Void DrawBenzier(int n, CPoint P[ ]) { CPoint PT; float dt,t,m; int I;t=0; m=100; dt=1/m; Moveto(P[0].x,P[0].y); For(i=1;i<=m;i++){ Pt=TPt(P,t,n); Lineto(Pt.x,Pt.y); t=t+dt; } Các mặt có quy tắc: (mặt trụ) Mặt trụ là mặt được tạo ra khi một dường thẳng (đường sinh) được quét dọc theo một đường cong P0(u) (đường chuẩn). đường cong P0(u) nằm trên một mặt phẳng nào đó . Gọi d là đường sinh,d=const. P0(u) là đáy dưới. P1(u)là đáy trên. Suy ra d= P0(u)- P1(u)p(u,v)= (1-v).P0(u)+v.P1(u) = P0(u)+(P1(u) -P0(u)).v= P0(u)+d.v Vậy: p(u,v)= P0(u)+d.v Pt mặt trụ : p(u,v)= P0(u)+d.v Dạng quen thuộc của mặt trụ là mặt trụ trònL trong mặt phẳng xoy, lấy P0(u) là đường tròn tâm o bán kính r. Ta có:d=(0,0,h) P0(u)=(r.cos(u),r.sin(u),0) Vậy: {X(u,v)=r.cos(u) Y(u,v)=r.sin(u) Z(u,v)=h.v với {0<=v<=1; 0<=u<=2π thủ tục mặt trụ: void DrawCylinder( float R,float h){ Point3D P; Point2D P1 ; doule Delta_U, Delta_V,u,v; Delta_U=0.06 ; Delta_V=0.03; for(u=0;u<2*M_PI;u+= Delta_U) { for(v=0;v<2*M_PI;v+= Delta_V) { P.x=R*cos(u); P.y=R*sin(u); P.z=v*h; P1=Chieu(KieuChieu,P); Putpixel(xc+P1.x+P1.y,Ưhite);}}} Mặt nón: Mặt nón là mặt được tạo ra khi một dường thẳng di chuyển dọc theo một đường cong phawmgr cho trước. các duowgf thẳng luôn di qua một điểm cố định gọi là đỉnh của mặt nón. P0(u) trùng với gốc tọa độ O. P1(u) là đường trong tâm (0,0,h) bán kính r. P0(u)=(r.cos(u),r.sin(u),h) Ta có: p(u,v)=(1-v). P0(u)+ v. P1(u)=v. P0(u) Vạy: p(u,v)= v. P0(u). Hay: {X(u,v)=v.r.cos(u) Y(u,v)=v.r.sin(u) Z(u,v)=h.v với {0<=v<=1; 0<=u<=2π thủ tục mặt nón: void DrawCone( float R,float h){ Point3D P; Point2D P1 ; doule Delta_U, Delta_V,u,v; Delta_U=0.03 ; Delta_V=0.1; for(u=0;u<2*M_PI;u+= Delta_U) { for(v=0;v<2*M_PI;v+= Delta_V) { P.x=v.R*cos(u); P.y=v.R*sin(u); P.z=v*h; P1=Chieu(KieuChieu,P); Putpixel(xc+P1.x+P1.y,White);}}} Các mặt tròn xoay(mặt cầu): Với mặt cầu tâm O bán kính r thì C là ½ đường tròn trong mặt phawmgr xoz. Ta có: X(v)=r.cos(u); Z(v)=r.sin(v); {X(u,v)=r.cos(u).cos(v) Y(u,v)=r.sin(u).cos(v) Z(u,v)=r.sin(v) với {- π /2<=v<= π /2 ; 0<=u<=2π thủ tục mặt cầu: void DrawSphere( float R){ Point3D P; Point2D P1 ; doule Delta_U, Delta_V,u,v,Pi_2; Pi_2=M_PI/2; Delta_U=0.1,;Delta_V=0.1; Delta_U=0.03 ; Delta_V=0.1; { for(v= -Pi_2;v<2*M_PI;v+= Delta_V) for(u=0;u<2*M_PI;u+= Delta_U) { P.x= R* sin(u).cos(v); P.y=R* sin(u).cos(v); P.z=R.sin(v); P1=Chieu(KieuChieu,P); Putpixel(xc+P1.x+P1.y,GREEN); }}}
Tài liệu liên quan