文章目录
计算机图形学作业
计算机图形学作业实现了以下三个功能:
- 画线,画圆,画椭圆
(1)中点法,DDA法,Bresenham法画线
(2)中点法,Bresenham法画圆
(3)中点法画椭圆 - 绘制多边形及填充多边形(内点表示递归填充)
- 绘制3D茶壶、圆环、正方体以及实现光照效果
源码:链接:https://pan.baidu.com/s/1FvM2IPcZDqPKB-aEtLGkMw
提取码:01bk
复制这段内容后打开百度网盘手机App,操作更方便哦
*以上功能都实现了与鼠标交互,即鼠标操作画线画圆等
*实现语言为C,平台为VC6.0,提前导入openGL
一、画线,画圆,画椭圆
1.中点法,DDA法,Bresenham法画线
int count=pDoc->Q.size();//Q队列
while (count--)
{
p=pDoc->Q.front();//队头删除队尾插入
pDoc->Q.pop();
switch(p.Paint_Type)
{
case 1:
{
//DDA画线法
int x,y;
float dx,dy,k,xi,yi;
dx=p.end.x-p.start.x;
dy=p.end.y-p.start.y;
xi=p.start.x;
yi=p.start.y;
k=dy/dx;
if (k<=1 && k>=-1)
{
if (p.start.x<p.end.x)
for (x=p.start.x;x<=p.end.x;x++)
{
pDC->SetPixel(x,int (yi+0.5),p.Line_Color);
yi=yi+k;
}
else
for (x=p.start.x;x>=p.end.x;x--)
{
pDC->SetPixel(x,int (yi+0.5),p.Line_Color);
yi=yi-k;
}
}
else
if (k>1 || k<-1)
{
k=dx/dy;
if (p.start.y<p.end.y)
for (y=p.start.y;y<=p.end.y;y++)
{
pDC->SetPixel(int (xi+0.5),y,p.Line_Color);
xi=xi+k;
}
else
for (y=p.start.y;y>=p.end.y;y--)
{
pDC->SetPixel(int (xi+0.5),y,p.Line_Color);
xi=xi-k;
}
}
pDoc->Q.push(p);
break;
}
case 2:
{
//Bresenham画线法
int x,y,e;
double dx,dy,k;
dx=p.end.x-p.start.x;
dy=p.end.y-p.start.y;
k=dy/dx;
int i;
if(k>0 && k<1)
{
if(p.end.x>=p.start.x)
{
x=p.start.x;
y=p.start.y;
e=-dx;
for(i=0; i<=dx; i++)
{
pDC->SetPixel(x,y,p.Line_Color);
x++;
e=e+2*dy;
if(e>=0)
{
y++;
e=e-2*dx;
}
}
}
else
{
x=p.end.x;
y=p.end.y;
e=dx;
for(i=0; i<=(-dx); i++)
{
pDC->SetPixel(x,y,p.Line_Color);
x++;
e=e-2*dy;
if(e>=0)
{
y++;
e=e+2*dx;
}
}
}
}
else
if(k>1)
{
if(p.end.x>=p.start.x)
{
x=p.start.x;
y=p.start.y;
e=-dy;
for(i=0; i<=dy; i++)
{
pDC->SetPixel(x,y,p.Line_Color);
y++;
e=e+2*dx;
if(e>=0)
{
x++;
e=e-2*dy;
}
}
}
else
{
x=p.end.x;
y=p.end.y;
e=dy;
for(i=0; i<=(-dy); i++)
{
pDC->SetPixel(x,y,p.Line_Color);
y++;
e=e-2*dx;
if(e>=0)
{
x++;
e=e+2*dy;
}
}
}
}
else
if(k<0 && k>=-1)
{
if(p.end.x<=p.start.x)
{
x=p.start.x;
y=p.start.y;
e=-dx;
for(i=0; i>=dx; i--)
{
pDC->SetPixel(x,y,p.Line_Color);
x--;
e=e+2*dy;
if(e>=0)
{
y++;
e=e+2*dx;
}
}
}
else
{
x=p.end.x;
y=p.end.y;
e=dx;
for(int i=0; i>=-dx; i--)
{
pDC->SetPixel(x,y,p.Line_Color);
x--;
e=e-2*dy;
if(e>=0)
{
y++;
e=e-2*dx;
}
}
}
}
else
if(k<-1)
{
if(p.end.x<=p.start.x)
{
x=p.start.x;
y=p.start.y;
e=-dy;
for(i=0; i<=dy; i++)
{
pDC->SetPixel(x,y,p.Line_Color);
y++; e=e-2*dx;
if(e>=0)
{ x--; e=e-2*dy;}
}
}
else
{
x=p.end.x;
y=p.end.y;
e=dy;
for(i=0; i<=-dy; i++)
{
pDC->SetPixel(x,y,p.Line_Color);
y++;
e=e+2*dx;
if(e>=0)
{
x--;
e=e+2*dy;
}
}
}
}
pDoc->Q.push(p);
break;
}
case 3:
{
//中点画线法
int d1,d2,d,x,y;
double a,b,k;
k=double(p.start.y-p.end.y)/(p.start.x-p.end.x);
if (k>=0)
{
if( p.start.x<p.end.x )
{
a=p.start.y-p.end.y;
b=p.end.x-p.start.x;
x=p.start.x;
y=p.start.y;
}
else
{
a=p.end.y-p.start.y;
b=p.start.x-p.end.x;
x=p.end.x; y=p.end.y;
}
}
else
{
if(p.start.x<p.end.x )
{
a=p.end.y-p.start.y;
b=p.start.x-p.end.x;
x=p.end.x;
y=p.end.y;
}
else
{
a=p.start.y-p.end.y;
b=p.end.x-p.start.x;
x=p.start.x;
y=p.start.y;
}
}
if(k>=0 && k<=1)
{
d=2*a+b;
d1=2*a;
d2=2*(a+b);
pDC->SetPixel(x,y,p.Line_Color);
if(p.start.x<p.end.x)
{
while(x<p.end.x)
{
if(d<0)
{
x++;
y++;
d+=d2;
}
else
{
x++;
d+=d1;
}
pDC->SetPixel(x,y,p.Line_Color);
}
}
else
while(x<p.start.x)
{
if(d<0)
{
x++;
y++;
d+=d2;
}
else
{
x++;
d+=d1;
}
pDC->SetPixel(x,y,p.Line_Color);
}
}
else
if(k>1)
{
d=a+2*b;
d1=2*(a+b);
d2=2*b;
pDC->SetPixel(x,y,p.Line_Color);
if(p.start.x<p.end.x)
{ while(x<p.end.x)
{
if(d<0)
{ y++; d+=d2; }
else
{ x++; y++; d+=d1; }
pDC->SetPixel(x,y,p.Line_Color);
}
}
else
{
while(x<p.start.x)
{
if(d<0)
{
y++;
d+=d2;
}
else
{
x++;
y++;
d+=d1;
}
pDC->SetPixel(x,y,p.Line_Color);
}
}
}
else
if(k>-1 && k<0)
{
d=b-2*a;
d1=(-2)*a;
d2=2*(b-a);
pDC->SetPixel(x,y,p.Line_Color);
if(p.start.x>p.end.x)
{
while(x>p.end.x)
{
if(d<0)
{
x--;
y++;
d-=d2;
}
else
{
x--;
d-=d1;
}
pDC->SetPixel(x,y,p.Line_Color);
}
}
else
while(x>p.start.x)
{
if(d<0)
{
x--;
y++;
d-=d2;
}
else
{
x--;
d-=d1;
}
pDC->SetPixel(x,y,p.Line_Color);
}
}
else
if(k<-1)
{
d=2*b-a;
d1=2*(b-a);
d2=2*b;
pDC->SetPixel(x,y,p.Line_Color);
if(p.start.x>p.end.x)
while(x>p.end.x)
{
if(d<0)
{
y++;
d-=d2;
}
else
{
x--;
y++;
d-=d1;
}
pDC->SetPixel(x,y,p.Line_Color);
}
else
while(x>p.start.x)
{
if(d<0)
{
y++;
d-=d2;
}
else
{
x--;
y++;
d-=d1;
}
pDC->SetPixel(x,y,p.Line_Color);
}
}
pDoc->Q.push(p);
break;
}
2.中点法,Bresenham法画圆
case 4://中点法画圆
{
double R=sqrt((p.start.x-p.end.x)*(p.start.x-p.end.x)+(p.start.y-p.end.y)*(p.start.y-p.end.y));
double d;
int x,y,Rx,Ry;
x=0;
y=R;
d=1.25-R;
Rx=p.start.x;
Ry=p.start.y;
pDC->SetPixel(x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,-x+Ry,p.Line_Color);
pDC->SetPixel(x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,-x+Ry,p.Line_Color);
while (x<=y)
{
if (d<0)
d+=2*x+3;
else
{
d+=2*(x-y)+5;
y--;
}
x++;
pDC->SetPixel(x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,-x+Ry,p.Line_Color);
pDC->SetPixel(x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,-x+Ry,p.Line_Color);
}
pDoc->Q.push(p);
break;
}
case 6://Bresenham法画圆
{
double R=sqrt((p.start.x-p.end.x)*(p.start.x-p.end.x)+(p.start.y-p.end.y)*(p.start.y-p.end.y));
double d;
int x,y,Rx,Ry;
x=0;
y=R;
d=3-2*R;
Rx=p.start.x;
Ry=p.start.y;
pDC->SetPixel(x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,-x+Ry,p.Line_Color);
pDC->SetPixel(x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,-x+Ry,p.Line_Color);
while (x<=y)
{
if (d<0)
d+=4*x+6;
else
{
d+=4*(x-y)+10;
y--;
}
x++;
pDC->SetPixel(x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(y+Rx,-x+Ry,p.Line_Color);
pDC->SetPixel(x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,x+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-y+Rx,-x+Ry,p.Line_Color);
}
3.中点法画椭圆
case 5:
{
double a,b;
double Rx,Ry;
Rx=(p.start.x+p.end.x)/2;
Ry=(p.start.y+p.end.y)/2;
a=fabs(p.start.x-p.end.x)/2;
b=fabs(p.start.y-p.end.y)/2;
int x,y;
float d1,d2;
x=0;
y=b;
d1=b*b +a*a*(-b+0.25); //判别式d1的初始值
pDC->SetPixel(x,y,p.Line_Color);
while(b*b*x<a*a*y) //上部分开始
{
if(d1<0) //正右方
{
d1+=b*b*(2*x+3);
x++;
}
else //右下方
{
d1 +=(b*b*(2*x+3)+a*a*(-2*y+2));
x++;
y--;
}
pDC->SetPixel(x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,-y+Ry,p.Line_Color);
}
d2=b*b*(x+0.5)*(x+0.5)+a*a*(y-1)*(y-1)-a*a*b*b;
while(y >0)
{
if(d2<0) //右下方
{
d2 +=b*b*(2*x+2)+a*a*(-2*y+3);
x++;
y--;
}
else //正下方
{
d2+=a*a*(-2*y+3);
y--;
}
pDC->SetPixel(x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(x+Rx,-y+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,y+Ry,p.Line_Color);
pDC->SetPixel(-x+Rx,-y+Ry,p.Line_Color);
}
pDoc->Q.push(p);
break;
}
二、绘制多边形及填充多边形(内点表示递归填充)
1.绘制多边形,当某个点与第一个点半径小于等于50时自动闭合
CDC *pDC;
pDC=GetDC();
pDC->SetPixel(point.x,point.y,Col);
Polygon.push_back(point);//把当前点加入Polygon
int sum = Polygon.size();//获得Polygon大小,即多边形边数
if(sum < 2){//如果边数<2,只画当前点
pDC->SetPixel(point.x,point.y,Col);
}
else{//当边数>=2时,对当前点与Polygon[sum-2](即上一个点)画线连接
CDC* pDC = GetDC();
//起点
pDC->MoveTo(Polygon[sum-2]);
//划线,终点
pDC->LineTo(point);
//自动闭合
if(sqrt((point.x-Polygon[0].x)*(point.x-Polygon[0].x)+(point.y-Polygon[0].y)*(point.y-Polygon[0].y))<=50)
{
pDC->MoveTo(point);
pDC->LineTo(Polygon[0]);
}
ReleaseDC(pDC);
2.填充多边形
首先建立分类表及活动边表,进行多边形扫描转化,即将多边形顶点转换为点阵表示,也就是求出多边形内部的各个像素点并设置颜色和灰度。
void CMyPaintView::DrawPolygon(CDC *pDC)
{
CMyPaintDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int i,j;
Y_min=GetMin();
Y_max=GetMax();
//初始化AET表,即初始化活动边表
AET *pAET=new AET;
pAET->next=NULL;
//初始化NET表,即初始化分类表
NET *pNET[800];
for (i=0;i<=Y_max;i++)
{
pNET[i]=new NET;
pNET[i]->next=NULL;
}
for (i=0;i<=Y_max;i++)
for (j=0;j<Polygon.size();j++)
{
if(Polygon[j].y==i)
{
if(Polygon[(j-1+Polygon.size())%Polygon.size()].y>Polygon[j].y)
{
NET *p=new NET;
p->x=Polygon[j].x;
p->y_max=Polygon[(j-1+Polygon.size())%Polygon.size()].y;
p->dx=float(Polygon[(j-1+Polygon.size())%Polygon.size()].x-Polygon[j].x)/float(Polygon[(j-1+Polygon.size())%Polygon.size()].y-Polygon[j].y);
p->next=pNET[i]->next;
pNET[i]->next=p;//pNET[i]中插入p结点
}
if(Polygon[(j+1+Polygon.size())%Polygon.size()].y>Polygon[j].y)
{
NET *p=new NET;
p->x=Polygon[j].x;
p->y_max=Polygon[(j+1+Polygon.size())%Polygon.size()].y;
p->dx=float(Polygon[(j+1+Polygon.size())%Polygon.size()].x-Polygon[j].x)/float(Polygon[(j+1+Polygon.size())%Polygon.size()].y-Polygon[j].y);
p->next=pNET[i]->next;
pNET[i]->next=p;
}
}
}
for(i=0;i<=Y_max;i++)
{
NET *p=pAET->next; //更新AEL
while(p)
{
p->x=p->x + p->dx;
p=p->next;
}
AET *tq=pAET; //AEL排序
p=pAET->next;
tq->next=NULL;
while(p)
{
while(tq->next && p->x >= tq->next->x)
tq=tq->next;
NET *s=p->next;
p->next=tq->next;
tq->next=p;
p=s;
tq=pAET;
}
AET *q=pAET; //删除表中ymax==i的结点
p=q->next;
while(p)
{
if(p->y_max==i)
{
q->next=p->next;
delete p;
p=q->next;
}
else
{
q=q->next;
p=q->next;
}
}
p=pNET[i]->next; //将NET中的新点加入AEL,并用插入法按X值递增排序
q=pAET;
while(p)
{
while(q->next && p->x >= q->next->x)
q=q->next;
NET *s=p->next;
p->next=q->next;
q->next=p;
p=s;
q=pAET;
}
p=pAET->next; //配对填充颜色
while(p && p->next)
{
for(float j=p->x;j<=p->next->x;j++)//可以改下试试
pDC->SetPixel(int(j),i,Col);
p=p->next->next;
}
}
Polygon.clear();
}
内点填充法实现:
void CMyPaintView::InnerPoint_Fill(int x,int y,COLORREF oldcolor,COLORREF newcolor)
{
CDC *pDC;
pDC=GetDC();
if (pDC->GetPixel(x,y)==oldcolor)
{
pDC->SetPixel(x,y,newcolor);
InnerPoint_Fill(x,y+1,oldcolor,newcolor);
InnerPoint_Fill(x-1,y,oldcolor,newcolor);
InnerPoint_Fill(x,y-1,oldcolor,newcolor);
InnerPoint_Fill(x+1,y,oldcolor,newcolor);
}
}
在这里有一个bug,填充时对多边形边的处理效果不好,推测是因为分类表及活动边表的建立有点问题,倒数第九行for(float j=p->x;j<=p->next->x;j++)可以把循环条件改改,改成从p->x+1到p->next->x或p->next->x-1等等,可以尝试改一下,效果会好一点。
三、绘制3D茶壶、圆环、正方体以及实现光照效果
1.绘制3D茶壶、圆环、正方体
用OpenGL自带函数即可实现:
glutSolidTeapot(1.0);//画茶壶
glutSolidTorus(0.3,0.7, 100, 100);//画圆环
glutSolidCube(1);//画正方体
2.实现光照
void CMyPaintView::SetupLighting()//设置灯光
{
GLfloat matSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f};//镜面光颜色
GLfloat matShininess[] = { 50.0f};//镜面光亮度
GLfloat matAmbient[] = { 0.0f, 0.0f, 0.0f, 1.0f};//环境光
GLfloat matDiffuse[] = { 0.6f, 0.0f, 0.8f, 1.0f};//漫反射
glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);//定义材质
glMaterialfv(GL_FRONT, GL_SHININESS, matShininess);
glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiffuse);
glMaterialfv(GL_FRONT, GL_AMBIENT, matAmbient);
//Lighting Parameters
//Enable Lighting
glEnable(GL_LIGHTING);
//Specify a single directional light
GLfloat diffuse1[] = { 1.0f, 1.0f, 1.0f};
GLfloat specular1[] = { 1.0f, 1.0f, 1.0f};
GLfloat position1[] = { 0.0f,0.0f,5.0f,0.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse1);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular1);
glLightfv(GL_LIGHT0, GL_POSITION, position1);
glEnable(GL_LIGHT0);
}
3.实现旋转
鼠标移动时实现旋转:
void CMyPaintView::OnMouseMove(UINT nFlags, CPoint point)
{
CClientDC dc(this);
dc.SetROP2(R2_NOTXORPEN);
if(true==bt_down)
{
x_Angle_3D+=(point.y-m_MouseDownPoint.y)/3.6;
y_Angle_3D+=(point.x-m_MouseDownPoint.x)/3.6;
InvalidateRect(NULL,FALSE);
m_MouseDownPoint=point;
}
CView::OnMouseMove(nFlags, point);
}
实现时:
glRotatef(x_Angle_3D, 1.0f,0.0f,0.0f);
glRotatef(y_Angle_3D, 0.0f,1.0f,0.0f);
总结
才疏学浅,有什么问题欢迎评论区批评指正。