目录
学习目标
前情提要
# RGB颜色模型
HSV颜色模型
CMYK颜色模型/印刷颜色模型
简单光照模型(考)⭐⭐⭐
简单光照模型假定:
材质属性
环境光模型
漫反射光模型
镜面反射光模型
Phong反射模型:
光强衰减
增加颜色
光滑着色
直线段的光滑着色
Gouraud明暗处理
Phong明暗处理
简单透明模型(不考)
纹理映射
颜色纹理
1.函数纹理
图像纹理
三维纹理
几何纹理
映射原理
几何纹理的分类
纹理反走样
学习目标
前情提要
使用透视投影绘制的三维物体已经具有近大远小的立体效果,经过背面剔除和z-buffer消隐后,初步生成了具有较强立体感的图形,但要模拟真实物体,还必须为其表面添加材质、映射纹理、施加光照、绘制阴影后才能产生真实感图形(photorealism computer graphics)。
三原色有这样的两个性质:
- 以适当比例混合可以得到白色,三原色中的任意两种原色的组合都得不到第三种原色;
- 通过三原色的混合可以得到可见光谱中的任何一种颜色。
计算机图形学中常用的颜色模型有RGB颜色模型、HSV颜色模型和CMYK颜色模型等。其中颜色模型RGB和CMYK是最基础的模型,其余的颜色模型在显示时都需要转换为RGB模型,在打印或印刷时都需要转换为CMYK模型。
对于发光体使用的是RGB加色系统,对于反射体使用的是CMY减色系统。
加色系统中,通过对颜色分量的叠加产生新颜色。
红色和绿色等量叠加成为黄色,红色和蓝色等量叠加成为品红;
绿色和蓝色等量叠加成为青色;
如果红色、绿色和蓝色等量叠加,则成为白色。
减色系统中,通过消除颜色分量来产生新颜色。
当在纸面上涂上品红油墨时,该纸面就不反射绿光;
当在纸面上涂上黄色油墨时,该纸面就不反射蓝光;
当在纸面上涂上青色油墨时,该纸面就不反射红光;
如果在纸面上涂上了品红油墨、黄色油墨和青色油墨,那么所有的红光、绿光和蓝光都被吸收,纸面呈现黑色。
# RGB颜色模型
RGB颜色模型是显示器的物理模型,无论软件开发中使用何种颜色模型,只要是绘制到显示器上,图像最终是以RGB颜色模型表示的。
class CRGBA
{
public:
CRGBA ();
virtual ~CRGBA ();
public: //分量取值范围〔0,1〕
double red; //红色分量
double green; //绿色分量
double blue; //蓝色分量
double alpha; //alpha分量
};
HSV颜色模型
HSV颜色模型是一种直观的颜色模型,包含三个要素:色调(hue)、饱和度(saturation)和明度(value)。
色调H是一种颜色区别于其它颜色的基本要素,如红、橙、黄、绿、青、蓝、紫等,当人们谈论颜色时,实际上是指它的色调,特别地,黑色和白色无色调。
饱和度S是指颜色的纯度。没有与任何颜色相混合的颜色,其纯度为全饱和。
要想降低饱和度可以在当前颜色中加入白色,鲜红色饱和度高,粉红色饱和度低。
明度V是颜色的相对明暗程度。
要想降低明度则可以在当前颜色中加入黑色,明度最高得到纯白,最低得到纯黑。
利用理论去研究图片:
CMYK颜色模型/印刷颜色模型
CMYK也称作印刷颜色模型,顾名思义就是用来印刷的。
在印刷品上看到的图像,就使用了CMYK模型。其中K表示黑色(black),之所以不使用黑色的首字母B,是为了避免与蓝色(Blue)混淆。
从理论上讲,只需要CMY三种油墨就足够了,浓度为100%的三种油墨加在一起就可以得到黑色。但是由于目前工艺还不能造出高纯度的油墨,CMY相加的结果实际是一种“灰”黑色。
同时,由于使用一种黑色油墨要比使用青色、品红和黄色三种油墨便宜,所以黑色油墨被用于代替等量的青色、品红和黄色油墨。这就是四色套印工艺采用的CMYK模型的理由。
因此,打印和印刷,这两者是有很大区别的。
打印一般数量很少,质量和速度要求也不高。常见于个人及小型办公使用。
印刷则正相反。随着印刷技术的进步,现在很多中小型印刷厂都采用了四色印刷机,降低了废品率。
简单光照模型(考)⭐⭐⭐
光照是增强图形真实感的重要技术。
光照模型是根据光学物理的有关定律,计算在特定光源的照射下,物体表面上一点投向视点的光强。
光线投射到物体表面时,可能被物体吸收、反射或透射(折射)。
其中被吸收的入射光转化为热,其余部分则向四周反射或透射。
朝向视点的反射光和透射光进入视觉系统,使物体可见。
计算机图形学的光照模型分为局部光照模型(local illumation model)和全局光照模型。
局部光照模型仅考虑光源直接照射到物体表面所产生的效果,通常假设物体表面不透明且具有均匀的反射率。
简单光照模型是局部光照模型的一种经验模型,认为镜面反射与物体表面的材质无关、物体的颜色由材质的漫反射率决定。这使得所生成的图像看上去像塑料,显示不出磨亮的金属光泽。
简单光照模型假定:
简单光照模型只考虑物体对直接光照的反射作用,而物体之间的反射作用,用环境光(ambient light)常量统一表示。简单光照模型分为环境光模型、漫反射光模型和镜面反射光模型,全部属于经验模型。
材质属性
物体的材质是指物体表面对光的吸收、反射和透射的性能。|| 什么?材质是性能?
由于研究的是简单局部光照模型,所以只考虑材质的反射属性。
同光源一样,材质也由环境色、漫反射色和镜面反射色等分量组成,分别说明了物体对环境光、漫反射光和镜面反射光的反射率。
材质决定物体的颜色,在进行光照计算时,材质对环境光的反射率与光源的环境光分量相结合,对漫反射光的反射率与光源的漫反射光分量相结合,对镜面光的反射率与光源的镜面反射光分量相结合。
由于镜面反射光影响范围很小,而环境光是常数,所以物体的颜色由材质的漫反射率决定。
环境光模型
环境光是环境中其它物体散射到物体表面后再反射出来的光。
由周围物体多次反射所产生的环境光来自周围各个方向,又均匀地向各个方向反射。
环境光的反射光强Ie可表示为:
漫反射光模型
镜面反射光模型
镜面反射光是只朝一个方向反射的光,具有很强的方向性,并遵守反射定律 。
镜面反射光会在光滑物体表面形成一片非常亮的区域,称为高光(hilight)区域。
Phong反射模型:
在简单光照模型中,镜面反射光颜色和入射光颜色相同,也即镜面反射光只反映光源的颜色。在白光的照射下,物体的高光区域显示白色;在红光的照射下,物体的高光区域显示红色。镜面光反射率ks是一个与物体颜色无关的参数。
镜面反射光光强不仅取决于物体表面的法线方向,而且依赖于光源和视点的相对位置。只有当视点位于比较合适的位置时,才可以观察到物体表面某些区域呈现出高光,当视点位置改变时,高光区域也会随之消失。
光强衰减
增加颜色
由于计算机中采用的是RGB颜色模型,因此需要为颜色的红、绿、蓝3个分量分别建立光照模型。
计算所得的颜色要归一化到区间 [0,1] 后,才能在RGB颜色模型中正确显示。
#define MIN(a,b) ((a<b)?(a):(b))
#define MAX(a,b) ((a>b)?(a):(b))
CRGB CLighting::Lighting(CP3 ViewPoint,CP3 Point,CVector Normal,
CMaterial *pMaterial)
{
CRGB LastC=pMaterial->M_Emit;//材质自身发散色为初始值
for(int i=0;i<LightNum;i++)//来自光源
{
if(Light[i].L_OnOff)
{
CRGB InitC;
InitC.red=0,InitC.green=0,InitC.blue=0;
CVector VL(Point,Light[i].L_Position);
//VL为指向光源的矢量
double d=VL.Mag();//d为光传播的距离,等于光矢量VL的模
VL=VL.Normalize();//单位化光矢量
CVector VN=Normal;
VN=VN.Normalize();//单位化法矢量
//第1步,加入漫反射光
double CosTheta=MAX(Dot(VL,VN),0);
InitC.red+=Light[i].L_Diffuse.red*pMaterial->M_Diffuse.red*CosTheta;
InitC.green+=Light[i].L_Diffuse.green*pMaterial->M_Diffuse.green*CosTheta;
InitC.blue+=Light[i].L_Diffuse.blue*pMaterial->M_Diffuse.blue*CosTheta;
//第2步,加入镜面反射光
CVector VS(Point,ViewPoint);//VS为视矢量
VS=VS.Normalize();//单位化视矢量
CVector VH=(VL+VS)/(VL+VS).Mag();//VH为平分矢量
double nHN=pow(MAX(Dot(VH,VN),0),pMaterial->M_n);
InitC.red+=Light[i].L_Specular.red*pMaterial->M_Specular.red*nHN;
InitC.green+=Light[i].L_Specular.green*pMaterial->M_Specular.green*nHN;
InitC.blue+=Light[i].L_Specular.blue*pMaterial->M_Specular.blue*nHN;
//第3步,光强衰减
double c0=Light[i].L_C0;//c0为常数衰减因子
double c1=Light[i].L_C1;//c1线性衰减因子
double c2=Light[i].L_C2;//c2二次衰减因子
double f=(1.0/(c0+c1*d+c2*d*d));//光强衰减函数
f=MIN(1.0,f);
LastC+=InitC*f;
}
else
LastC+=Point.c;//物体自身颜色
}
//第4步,加入环境光
LastC+=Ambient*pMaterial->M_Ambient;
//第5步,颜色归一化到[0,1]区间
LastC.Normalize();
//第6步,返回所计算顶点的光强颜色
return LastC;
}
光滑着色
直线段的光滑着色
Gouraud明暗处理
先计算物体表面多边形各顶点的平均法矢量,
然后调用简单光照模型计算各顶点的光强,多边形内部各点的光强则通过对多边形顶点光强的双线性插值得到。
Gouraud明暗处理的实现步骤如下:
(1)计算多边形顶点的平均法矢量。
(2)对多边形网格的每个顶点根据简单光照模型计算光强。
(3)根据每个多边形网格的顶点的光强,按照扫描线顺序使用线性插值计算多边形网格边上每一点的光强。
(4)在扫描线与多边形相交区间内,使用线性插值获得区间内每一点的光强。然后再将光强分解为该点的RGB颜色。
Gouraud明暗处理存在的缺陷 :
(1)使用Gouraud双线性光强插值实现相邻多边形之间的光滑过渡时,由于采用了光强插值,高光区域的多边形边界明显,马赫带效应没有完全消除。
(2)镜面反射的高光区域只能在最小面片的周围形成,不能在面片的内部形成,导致Gouraud明暗处理生成的高光区域明显大于Phong明暗处理生成的高光区域。
Gouraud插值算法使得图形变得光滑,主要原因是使用了相邻两个面的平均法矢量计算过渡光强,实现了“使用有限多边形构造光滑表面”的设计目标。
Phong明暗处理
先计算多边形网格的每个顶点的平均法矢量,
然后使用双线性插值计算多边形内部各点的法矢量。最后才使用多边形网格上各点的法矢量调用简单光照模型计算其所获得的光强。
Phong明暗处理的实现步骤如下:
⑴计算多边形顶点的平均法矢量。
⑵线性插值计算多边形内部各点的法矢量。
双线性法矢插值的计算公式与光强插值的类似,只是将其中的光强项用法矢项代替。
⑶对多边形内的每一点使用法矢量调用简单光照模型计算光强,然后再将光强分解为该点的RGB颜色。
简单透明模型(不考)
简单透明模型是不考虑折射的影响的经验模型。该模型简单地将物体1上各像素处的光强与其后的另一个物体2上相应像素处的光强作线性插值以确定物体1上各像素最终显示的光强。
简单阴影模型(不考)
纹理映射
现实世界中的物体表面存在丰富的纹理细节,人们正是依据这些纹理细节来区分各种具有相同形状的物体。
定义
为物体表面添加纹理的技术称为纹理映射(texture mapping)。
纹理映射是将纹理空间(texture space)坐标(u,v)映射为物体空间(object space)坐标(x,y,z),再进一步映射为屏幕图像空间(screen space)二维物体表面坐标(x,y)的过程,如图10-37所示。
当什么属性发生改变时,可以产生纹理效果呢?
漫反射光模型主要产生纹理效果。当光源的位置不变时,单位光矢量L是一个定值。影响光强的只有漫反射系数kd和单位法矢量N的方向。
1974年,Catmull 首先采用二维图像来定义物体表面材质的漫反射系数kd,这种纹理被称为颜色纹理。
1978年,Blinn提出了在光照模型中适当扰动物体表面的 单位法矢量N 的方向产生表面凹凸纹理的方法,被称为几何纹理。
凸凹纹理映射: 由Jim Blinn发明,用法向扰动技术模拟带褶皱的曲面。Bump mapping最初由Jim Blinn在1978年发明。
颜色纹理映射可以有两种实现方法,
一种方法是直接用纹理的颜色替代物体表面的颜色。在这种情况下,不必进行光照计算。
另一种方法是纹理数据参加光照计算。在这种情况下,物体表面的纹理会显示光照效果,但这会影响到镜面高光效果。
因为镜面反射光的颜色是由光源颜色决定的,而与物体本身材质颜色无关。
处理方法是将镜面反射光分离出来,通过设置材质漫反射系数kd完成纹理映射后,再将镜面反射光分量加上去。
颜色纹理
通过颜色变化表现出来的表面细节,称为颜色纹理。
- 颜色纹理难以直接构造,常采用函数纹理或图像纹理来描述表面细节。
- 为了使映射在物体表面的颜色纹理不因物体位置的改变而漂移,需要将颜色纹理绑定到物体的表面上。
- 一般采用物体表面的参数化方法来确定表面的纹理坐标。
1.函数纹理
纹理一般定义在单位正方形区域(0≤u≤1,0≤v≤1),称为纹理空间。
理论上,任何定义在此空间内的函数都可以作为纹理函数,
实际上,常采用一些特殊的函数来模拟现实世界中存在的纹理,如国际象棋棋盘纹理函数、粗布纹理函数等 。
double a,b;
for(double u=0.0;u<=1.0;u+=0.001)
for(double v=0.0;v<=1.0;v+=0.001)
{
if(0==(int(floor(u*8))+int(floor(v*8)))%2)//偶数
{
a=0.9;
pDC->SetPixelV(Round(u*500-250),Round(v*500-250)
,RGB(a*255,a*255,a*255));
}
else
{
b=0.1;
pDC->SetPixelV(Round(u*500-250),Round(v*500-250)
,RGB(b*255,b*255,b*255));
}
}
void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CRect rect;
GetClientRect(&rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(rect.Width(),rect.Height());
pDC->SetViewportExt(rect.Width(),-rect.Height());
pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);
double p=100,q=100;
for(double u=0.0;u<=1.0;u+=0.001)
for(double v=0.0;v<=1.0;v+=0.001)
{
double A=double(rand())/RAND_MAX;
double f=A*(cos(p*u)+cos(q*v));
pDC->SetPixelV(Round(u*500-250),Round(v*500-250),RGB(f*255,f*255,f*255));
}
}
图像纹理
函数纹理是使用数学方法定义的简单二维纹理图案,这种纹理规则而单调。
为了增强纹理的表现力,一个自然的想法是将一幅来自数码相机的二维图像作为纹理映射到物体上。
图像纹理映射需要建立物体表面上每一采样点与已知图像上各点(称为纹素,texel)的对应关系,取图像上点的颜色值作为物体表面上采样点的颜色值,然后采用光照模型来计算该点处的光强。
图像纹理映射既可以采用将图像纹理绑定到物体顶点上的方式实现也可以采用将图像纹理绑定到表面上的方式实现。
前者一般用于映射单幅图像,仅需要正确处理图像接缝,后者一般用于映射多幅图像。
对于立方体,由于使用6幅图像纹理,一般采用绑定到表面上的方式实现。读入立方体6个表面所对应的位图的代码如下:
void CTestView::ReadImage(int nface)//读入位图图像纹理
{
BYTE Texture[]={IDB_FRONT,IDB_BACK,IDB_LEFT,IDB_RIGHT
,IDB_TOP,IDB_BOTTOM};
CBitmap NewBitmap;
NewBitmap.LoadBitmap(Texture[nface]);//调入位图资源
NewBitmap.GetBitmap(&bmp);//获得CBitmap的信息
int nbytesize=bmp.bmWidthBytes*bmp.bmHeight;
im=new BYTE[nbytesize];
NewBitmap.GetBitmapBits(nbytesize,(LPVOID)im);
Image=new COLORREF*[bmp.bmHeight];
for(int n1=0;n1<bmp.bmHeight;n1++)
Image[n1]=new COLORREF[bmp.bmWidth];
for(n1=0;n1<bmp.bmHeight;n1++)//Height
{
for(int n2=0;n2<bmp.bmWidth;n2++)//Width
{
int pos=n1*bmp.bmWidthBytes+4*n2;
n1=bmp.bmHeight-1-n1
Image[bmp.bmHeight-1-n1][n2]
=RGB(im[pos+2],im[pos+1],im[pos]);
}
}
delete []Im;
}
圆柱面、圆锥面、球面和圆环面仅使用一幅位图就可以实现图像纹理映射,一般采用绑定到物体顶点上的方式实现。
三维纹理
由于纹理是二维的,而物体是三维的,很难在物体表面的连接处做到纹理自然过渡,极大地降低了图形的真实感。
假如在三维物体空间中,物体上的每一个点P(x,y,z)均有一个纹理值t(x,y,z),其值由纹理函数惟一确定。
那么对于物体上的空间点,就可以映射到一个定义了纹理函数的三维空间上了。
由于三维纹理空间与物体空间维数相同,在进行纹理映射时,只需把场景中的物体变换到纹理空间即可。
代码:
CRGB CZBuffer::ReadWoodTexture(CP3 t)
{
double Radius,Angle;
int Tex;
Radius=sqrt(t.x*t.x+t.z*t.z);
if(0==t.z)
Angle=PI/2;
else
Angle=atan(t.x/t.z);
Radius=Radius+2*sin(20*Angle+t.y/150);
Tex=Round(Radius)%60;
if(Tex<40)
return CRGB(0.8,0.6,0.0);
else
return CRGB(0.5,0.3,0.0);
}
几何纹理
现实世界中还存在另一类物体表面如橘子皮、树皮、混凝土墙面等凹凸不平的表面。虽然可以将上述位图作为颜色纹理映射到相应的几何表面以增加真实感,但却无法表达表面的凹凸不平。
凹凸纹理(bump map)的基本思想是用简单光照模型计算物体表面的光强时,对物体表面的法向进行微小的扰动,导致表面光强的突变,产生凹凸不平的真实感效果。
映射原理
由于粗糙表面的凹凸高度相对于表面尺寸一般要小的多,因而B很小,可以忽略不计。
几何纹理的分类
(1)偏移矢量凹凸纹理
(2)高度场凹凸纹理
由于Blinn方法不计算扰动后物体表面各顶点的新位置,而是直接计算扰动后表面新的法矢量,该方法难以在物体的轮廓线上表现出凹凸不平的效果。
为此Cook采用位移映射(displacement mapping)技术来克服上述缺陷。
位移映射方法通过沿表面法向扰动物体表面上个顶点的位置来模拟表面的粗糙不平的效果。
纹理反走样
纹理映射是将纹理图案映射到不同大小的物体表面上。
- 若投影得到的象素数目比原始纹理大,则需要把纹理图像放大;
- 若投影得到的象素数目比原始纹理小,则需要把纹理图像缩小。
对于立方体、圆柱等物体,当纹理图案与屏幕像素之间相匹配时,可以实现一对一的映射。
但是将二维纹理映射到曲面物体上时,如球面、圆环面等,会产生严重的走样。
可以借助于不同的反走样技术予以改善,其中最简单的纹理反走样技术是双线性内插法。