1-到第3章的直线扫描转换(没更新完)
- 习题1
- 知识积累
- 习题2
- 知识点
- 映射模式
- 使用GDI对象
- 习题3
- 知识积累
- 直线的中点Bresenham算法
习题1
1.计算机图形学的定义是什么?说明计算机图形学、图像处理和模式识别之间的关系。
答:
CG是计算机图形学的缩写。随着以计算机为主要工具进行视觉设计和生产的一系列相关产业的形成,国际上习惯将利用计算机技术进行视觉设计和生产的领域通称为CG。
计算机图形学是一门研究如何利用计算机表示、生成、处理和显示图形的学科。 图形主要分为两类,一类是基于线条表示的几何图形,另一类是基于材质、纹理和光照表示的真实感图形。 图形的表示方法有两种:参数法和点阵法。
一般将用参数法描述的图形仍然称为图形,将用点阵描述的图形称为图像。计算机图形学就是研究将图形的表示法从参数法转换为点阵法的一门学科,或者简单地说,计算机图形就是计算机产生的图像。
关系:
计算机图形学是研究如何利用计算机把描述图形的几何模型通过指定的算法转化为图像显示的一门学科;
图像处理主要是指对数字图像进行增强、去噪、复原、分割、重建、编码、存储、压缩和恢复等不同处理方法的学科;
模式识别是对点阵图像进行特征抽取,然后利用统计学方法给出图形描述的学科。
近年来,随着光栅扫描显示器的广泛应用,这3门学科之间的界限越来越模糊,甚至出现了相互渗透和融合,这3个学科是相互促进和发展的。
IEEE的定义为:Computer graphics is the art or science of producing graphical images with the aid of computer.
……(暂不更新)
知识积累
CG 计算机图形学 computer graphics
UI 图形化用户界面 user interface
CAD 计算机辅助设计 Computer Aided Design
CAM 计算机辅助制造 Computer Aided Manufacturing
计算机图形学之父——Ivan E. Sutherland
屏幕宽高比(Aspect Ratio): 显示设备中显示图像的横向尺寸与纵向尺寸的比例。
(考点)
1.5 图形显示器的发展及其工作原理
1.5.4 光栅扫描显示器
光栅扫描显示器电子束的强度可以不断变化,所以容易生成颜色连续变化的真实感图像。
光栅扫描显示器是画点设备,可看作是一个点阵单元发生器,并可控制每个点阵单元的亮度,这些点阵单元被称为像素(Picture Element,Pixel)。
荧光点图案:
帧缓冲器(frame buffer)是显存内用于存储一帧图像的一片连续内存空间。
光栅扫描显示器使用帧缓冲存储屏幕上每个像素的颜色信息,帧缓冲使用位面(bit plane)与屏幕像素一一对应。
删除线格式
如果屏幕上每个像素的颜色只用一位(Bit)表示,其值非0即1,屏幕只能显示黑白二色图像,称为黑白显示器,此时帧缓冲器只有一个位面。
如果每个像素的颜色可以用一个字节(Byte)表示,帧缓冲器需要用八个位面,可表示256种灰度,称为灰度显示器。 这时候还不是彩色的。
如果每个像素用R、G、B三原色混合表示,其中每种原色分别用一个字节表示,各对应一把电子枪,每种颜色可有256种亮度,三种颜色的组合是2^24颜色,共有24个位面。
索引色
习题2
知识点
例2-1 设计一个长方形CRectangle类,调用类的成员函数计算长方形的周长和面积。
#include <iostream.h>
class CRectangle
{
public:
CRectangle();
CRectangle(int width,int height);
~CRectangle();
double perimeter();
double area();
private:
int width;
int height;
};
CRectangle::CRectangle()
{
width=10;
height=5;
cout<<"建立默认对象"<<endl;
}
CRectangle::CRectangle(int width,int height)
{
this->width=width;
this->height=height;
cout<<"建立对象"<<endl;
}
CRectangle::~CRectangle()
{
cout<<"撤销对象"<<endl;
}
double CRectangle::perimeter() //周长
{
return 2*( width+height);
}
double CRectangle::area() //面积
{
return width*height;
}
void main()
{
CRectangle Rect1,Rect2(30,20),*pRect=&Rect2;
cout<<"Rect1的周长为:"<<Rect1.perimeter()<<endl;
cout<<"Rect1的面积为:"<<Rect1.area()<<endl;
cout<<"Rect2的周长为:"<<Rect2.perimeter()<<endl;
cout<<"Rect2的面积为:"<<Rect2.area()<<endl;
cout<<"Rect2的周长为:"<<pRect->perimeter ()<<endl;
cout<<"Rect2的面积为:"<<pRect->area()<<endl;
}
建立CRectangle类动态对象并初始化为宽度为40,高度为10的语句如下:
CRectangle* pRect=new CRectangle(40,10) ;
但使用new运算符创建数组时则无参数,并且只调用默认的构造函数。
例2-2 动态创建CRectangle类二维对象数组,输出每个对象的周长和面积。本例假定二维数组有2行3列。
void main()
{
int RowIndex=2,ColIndex=3;
CRectangle** pRect;
//创建二维动态数组
pRect=new CRectangle* [RowIndex];//设置行
for(int i=0;i<RowIndex;i++)
{
pRect[i]=new CRectangle[ColIndex];//设置列
}
//输出周长和面积
for(i=0;i<RowIndex;i++)
{
for(int j=0;j<ColIndex;j++)
{
cout<<"Rect["<<i<<","<<j<<"]
周长为"<<pRect[i][j].perimeter()<<",";
cout<<"面积为"<<pRect[i][j].area()<<endl;
}
}
//释放二维动态数组
for(int k=0;k<RowIndex;k++)
{
delete []pRect[k];
pRect[k]=NULL;
}
delete []pRect;
pRect=NULL;
}
CPaintDC对象只在响应WM_PAINT消息时使用。
CPaintDC类的构造函数会自动调用BeginPaint()函数。CPaintDC类的析构函数则会自动调用EndPaint()函数。
MFC程序中使用CPaintDC类在视图窗口中绘图时,
需要先添加WM_PAINT消息的映射函数OnPaint(),
然后在OnPaint()函数中编写与CPaintDC类相关的代码,而不是编写在OnDraw()中。
请注意,如果使用OnPaint()函数响应了WM_PAINT消息,则OnDraw()函数将会被自动屏蔽。
void CTestView::OnDraw(CDC* pDC)//绘制椭圆
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->Ellipse(100,70,900,570);
}
void CTestView::OnPaint() //绘制矩形
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
dc.Rectangle(100,70,900,570);
// Do not call CView::OnPaint() for painting messages
}
OnPaint() 屏蔽了OnDraw()函数,不绘制椭圆。如果删除此函数,则绘制结果为椭圆。
CWindowDC中点(0,0)在屏幕的左上角,而CClientDC和CPaintDC中点(0,0)是屏幕客户区的左上角。
如果在CTestView类中使用CWindowDC类对象进行绘图,只有在使用GetParent()函数获得CWnd指针后,才能在整个屏幕区域内绘图。
简单数据类型
CPoint类:存放点坐标(x,y)。
CRect类:存放矩形左上角顶点和右下角顶点的坐标(top,left,right,bottom)
CSize类:存放矩形的宽度和高度的坐标(cx,cy),其中cx为矩形的宽度,cy为矩形的高度。
绘图工具类
CGdiObject类:GDI绘图工具的基类,一般不能直接使用。
CBitmap:封装了一个GDI位图,提供位图操作的接口。
CBrush类:封装了GDI画刷,可以选作设备上下文的当前画刷。画刷用于填充图形内部。
CFont:封装了GDI字体,可以选作设备上下文中的当前字体。
CPallette:封装了GDI调色板,提供应用程序和显示器之间的颜色接口。
CPen:封装了GDI画笔,可以选作设备上下文的当前画笔。画笔用于绘制图形边界线。
删除线格式 删除线格式 删除线格式 删除线格式
注意!在选择GDI对象进行绘图时,需要遵循以下步骤:
- 绘图开始前,创建一个GDI新对象,并选入当前设备上下文,同时保存原GDI对象指针。
- 使用新GDI对象绘图。
- 绘图结束后,使用已保存的原GDI对象指针将设备上下文恢复原状。
- 如需重复使用同名新对象绘图,则需要先从内存中删除该对象后再使用。否则会有错误提示。
映射模式
把图形显示在屏幕坐标系中的过程称为映射
根据映射模式的不同可以分为
- 逻辑坐标
- 设备坐标
逻辑坐标的单位是米制尺度或其它与字体相关的尺度
设备坐标的单位是像素
使用GDI对象
创建画笔函数
- Cpen::CreatePen
- BOOL CreatePen(int nPenstyle,int nWidth,COLORREF col)
创建画刷函数
- (1)CBrush::CreateSolidBrush
BOOL CreateSolidBrush(COLORREF col)
- (2) CBrush::CreateHatchBrush
BOOL CreateHatchBrush(int nIndex COLORREF col)
- (3) CBrush::CreatePatternBrush
BOOL CreatePatternBrush(Cbitmap*pBitmap)
solid 英[ˈsɒlɪd] 美[ˈsɑːlɪd]
- n. 固体; 立体图形;
- adj. 固体的; 坚固的,坚硬的; 无空隙的; 非中空的,实心的; 结实的,牢固的; 质地坚实的,实在的; 纯质的,纯一的; 团结的,全体一致的; 精深的,扎实的; 彻底的,完全的; 可信赖的; 可敬的; 可靠的; 充分的,确实的; 资金雄厚的,殷实的; 不停顿的,持续不断的; 立体的,立方的; (样子或结构)浓密的; 丰盛的,重大的; 有力的; 精彩的,绝妙的; 使用固体燃料的; 相当不错的; 连续的; 纯色的; 立体的; <印>(行间)密排的,不用铅条隔开的“, ”<美口>关系很好的,关系密切的; 结实的,有力的…; 一致的; <澳口>苛刻的,不公正的;
- adv. 全部地,完全地; 不间断地,连续地; <美口>肯定,无疑;
COLORREF是32位颜色类型,crColor是像素点的颜色值,COLORREF型变量可以利用RGB(bRed,bGreen,bBlue)来指定相应的颜色值,每种颜色用一个字节表示,可以被设定为0~255之间的任意值,0代表无色,255代表全色。
习题3
知识积累
布雷森汉姆直线算法Bresenham算法。这里主要应用与直线的扫描转化。
尽量使用加减法(增量算法),
MoveTo与LineTo函数无法绘制光滑的(颜色渐变)走样或反走样直线。
直线的中点Bresenham算法
都很容易理解,都很简单。
中点误差项的值会导致什么呢?注意看上图↑
构造中点误差项
d的正负决定了y的取值。
看了这几个式子之后,其实就知道怎么一步步递推中点误差项的了。
中点误差项的初始值
其实这个递推公式,最根本的还是原隐函数的式子。
现有的研究已经证明:端点采用整数坐标没有什么益处,因为现在的CPU可以按照与处理整数同样的速度处理浮点数。因此本教材讲解的中点Bresenham算法采用了浮点数运算并且方便地解决了直线的反走样问题。
整个流程:
自定义函数代码;
void CTestView::BresenhamLine(CDC *pDC)
{
CPoint p0(-100,-50),p1(200,50),p;
int dx,dy;
dx=p1.x-p0.x;
dy=p1.y-p0.y;
double k,d;
k=(double)dy/dx;
d=0.5-k;
for(p=p0;p.x<p1.x;p.x++)//不包括终点(x1,y1)
{
pDC->SetPixelV(p,RGB(0,0,0));
if(d<0)
{
p.y++;
d+=1-k;
}
else
d-=k;
}
}
自带的画直线函数里进行构造:
void CTestView::CLine(CDC *pDC)
{
CPoint p0(-100,-50),p1(200,50),p;
int dx=p1.x-p0.x,dy=p1.y-p0.y;
double k,d;
k=(double)dy/dx;
d=0.5-k;
double dc=1/(double)dx;
for(p=p0;p.x<p1.x;p.x++)//不包括终点p1
{
double r,g,b;
r=(1-(p.x-p0.x)*dc),g=0,b=(p.x-p0.x)*dc;
pDC->SetPixelV(p,RGB(r*255,g*255,b*255));
if(d<0)
{
p.y++;
d+=1-k;
}
else
d-=k;
}
}