真实感图形的绘制

计算机图形学3D
真实感图形的绘制
真实感图形的相关理论介绍
1.1真实感图形介绍
真实感图形是计算机图形学中一个重要的组成部分,它的基本要求是在计算机中生成三维场景的真实感图形或图象。多媒体教育、虚拟现实系统、科学计算可视化、动画制作、电影特技模拟、计算机游戏等许多方面,真实感图形学都发 挥了重要的作用。其中的核心内容就是真实感绘制,它有多种方法,其中植物场景的真实感绘制是比较常用的。例如对 于场景中的物体、要得到它的真实感图形,就要对它进行透视投影,并消除隐藏面,然后计算可见面的光照明暗效果等。 关键词:真实感绘制技术;自然场景;体纹理;光线跟踪。
1.2颜色模型
红、绿、蓝三原色是基于人眼视觉颜色感知的三刺激理论设计的。三刺激理论认为,人眼的视网膜中有三种类型的视锥细胞,分别对红、绿、蓝三种色光最敏感。人眼光谱灵敏度实验曲线证明,这些光在波长为700nm (红色)、546 nm(绿色)和435.8 nm(蓝色)时的刺激点达到高峰。三原色有这样的两个性质:以适当比例混合可以得到白色,三原色中的任意两种原色的组合都得不到第三种原色;通过三原色的混合可以得到可见光谱中的任何一种颜色。
计算机图形学中常用的颜色模型有RGB颜色模型、HSV颜色模型和CMYK颜色模型等。其中颜色模型RGB和CMYK是最基础的模型,其余的颜色模型在显示时都需要转换为RGB模型,在打印或印刷时都需要转换为CMYK模型。
1.3光照模型
光照是增强图形真实感的重要技术。光照模型是根据光学物理的有关定律,计算在特定光源的照射下,物体表面上一点投向视点的光强。光线投射到物体表面时,可能被物体吸收、反射或透射(折射)。其中被吸收的入射光转化为热,其余部分则向四周反射或透射。朝向视点的反射光和透射光进入视觉系统,使物体可见。
Gouraud明暗处理:先计算物体表面多边形各顶点的平均法矢量,然后调用简单光照模型计算各顶点的光强,多边形内部各点的光强则通过对多边形顶点光强的双线性插值得到。
Phong明暗处理:先计算多边形网格的每个顶点的平均法矢量,然后使用双线性插值计算多边形内部各点的法矢量。最后才使用多边形网格上各点的法矢量调用简单光照模型计算其所获得的光强。
1.4纹理映射
现实世界中的物体表面存在丰富的纹理细节,人们正是依据这些纹理细节来区分各种具有相同形状的物体。
颜色纹理:通过颜色变化表现出来的表面细节,称为颜色纹理。颜色纹理难以直接构造,常采用函数纹理或图像纹理来描述表面细节。为了使映射在物体表面的颜色纹理不因物体位置的改变而漂移,需要将颜色纹理绑定到物体的表面上。一般采用物体表面的参数化方法来确定表面的纹理坐标。
图像纹理:图像纹理映射既可以采用将图像纹理绑定到物体顶点上的方式实现也可以采用将图像纹理绑定到表面上的方式实现。前者一般用于映射单幅图像,仅需要正确处理图像接缝,后者一般用于映射多幅图像。
几何纹理:现实世界中还存在另一类物体表面如橘子皮、树皮、混凝土墙面等凹凸不平的表面。虽然可以将上述位图作为颜色纹理映射到相应的几何表面以增加真实感,但却无法表达表面的凹凸不平。
凹凸纹理(bump map)的基本思想是用简单光照模型计算物体表面的光强时,对物体表面的法向进行微小的扰动,导致表面光强的突变,产生凹凸不平的真实感效果。
实现真实感图形的绘制的流程
使用透视投影绘制的三维物体已经具有近大远小的立体效果,经过背面剔除和z-buffer消隐后,初步生成了具有较强立体感的图形,但要模拟真实物体,还必须为其表面添加材质、映射纹理、施加光照、绘制阴影后才能产生真实感图形(Photorealism computer graphics)。
简单光照模型是一种经验模型,认为材质的漫反射率kd决定着物体的颜色,镜面高光只反映光源的颜色。由于简单光照模型认为镜面反射率与物体表面的材质无关,这使得物体看上去更像塑料。
为了实现曲面物体的光滑着色,常采用Gouraud明暗处理或Phong明暗处理。
Gouraud明暗处理是根据三角形或四边形面片的顶点颜色使用双线性插值计算面片内每一点的光强;
Phong明暗处理是根据面片顶点的法矢量使用双线性插值计算面片内每一点的法矢量后,才调用光照模型计算该点的光强。显然后者的计算代价远大于前者。透明处理是一种颜色融合的过程,一般通过颜色之间的线性插值实现。
阴影是一种将视点移动到光源位置的消隐过程。物体上只有从光源看过去不可见的,而从视点看过去可见的表面才能形成自身阴影,从光源到背光面顶点的光线投射在地面上的多边形形成投射阴影。
纹理映射是一个用函数、图像或其它数据源来改变表面外观的过程,一般需要先对物体的顶点或表面进行参数化处理,然后使用Phong明暗处理为物体表面添加纹理细节。
图像纹理改变的是物体材质的漫反射率,而几何纹理改变的是物体表面的法矢量方向。
凹凸纹理中,物体表面的法矢量保持不变,仅仅改变了在光照计算中的法矢量方向来此生凹凸不平的视觉效果。从几何角度讲,物体表面仍然保持光滑,正如同对多边形表面之间采用共享同一法矢量,可以在多边形表面之间产生光滑过渡的假象一样。反走样是纹理映射技术的一个重要研究内容,本章介绍了双线性内插法。
1.1界面的设计
在第一个程序中的界面主要分为两个部分,左边是一个功能控制界面, 右侧则是相应功能的显示界面。

 程序运行初始状态时的界面!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在第二个程序中将视图划分为四个区域,分别显示不同的模型
在这里插入图片描述

1.2功能的设计
第一个程序在左侧界面中可以通过不同的选项来调节光照模型,包括环境光、漫反射光和镜面反射光,此选项可以进行多项选择的叠加。图形主要有名字的线框模型、表面模型、正方体和球。明暗处理方式有Phong和Gourand处理。其次是物体材质的选择,可以选择金、红宝石、银和绿宝石的材质。最后是光源位置的选择,分别为左上、右上、左下和右下。在右侧界面中通过左侧功能的控制进行变换,也可通过菜单栏的选择进行转动,也可通过鼠标左键进行放缩。
第二个程序的主要功能是转动,点击相应的模块,通过上线的菜单选项可以让图形转动,也可通过鼠标的左键右键实现图形的放缩。

3.1界面的实现
第一个程序中界面的划分:在资源视图中的Dialog文件夹中新建一个dialog类,然后通过控件把左侧界面的按钮设计出来。左侧这一视图通过新建一个LeftPortion来控制。左后在CMainFrame类中进行分割,通过类向导创建函数
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
m_wndSplitter.CreateStatic(this,1,2);//产生1×2的静态切分窗格
m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CLeftPortion),CSize(260,600),pContext);
m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CMy1617417003klbView),CSize(520,600),pContext);
return TRUE;
// return CFrameWnd::OnCreateClient(lpcs, pContext);
}
第二个程序类的划分方式与第一个程序相同,4个视图区主要通过4个类来控制。CMy1617417003klbView类、CView1、Ciew2和CView3
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
CRect rc;
// 获取框架窗口客户区的CRect对象
GetClientRect(&rc);
// 创建静态分割窗口,两行一列
if (!m_wndSplitter.CreateStatic(this, 2, 2))
return FALSE;
if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CMy1617417003klbView), CSize(rc.Width()/2, rc.Height()/2), pContext))
return FALSE;
if (!m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CView1), CSize(rc.Width()/2, rc.Height()/2), pContext))
return FALSE;
if (!m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CView2), CSize(rc.Width()/2, rc.Height()/2), pContext))
return FALSE;
if (!m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CView3), CSize(rc.Width()/2, rc.Height()/2), pContext))
return FALSE;
return TRUE;
}

3.2功能的实现
第一个程序中通过双击相应的按钮实现对应的响应函数,此处以环境光的响应函数为例其余类似。各种模型的交互选择则通过一个参数来实现,当点击表面模型时,向向初始函数中传递参数值13,则在画图函数DrawObject()中执行mark=2时的情况。
void CLeftPortion::OnAmbient()
{
// TODO: Add your control notification handler code here
CMy1617417003klbDoc pDoc=(CMy1617417003klbDoc)CFormView::GetDocument();
pDoc->UpdateAllViews(NULL,1);
}
void CLeftPortion::OnBm()
{
// TODO: Add your control notification handler code here
CMy1617417003klbDoc pDoc=(CMy1617417003klbDoc)CFormView::GetDocument();
pDoc->UpdateAllViews(NULL,13);
}
void CMy1617417003klbView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
case 13:
mark=2;
break;
}
void CMy1617417003klbView::DrawObject(CDC *pDC)
{
if(mark2||mark3)
DrawBM(pDC);
}
void CMy1617417003klbView::DrawBM(CDC *pDC)
{
CPi3 Point[26];//透视投影后面的二维顶点数组

CZBuffer *zbuf=new CZBuffer;//申请内存
zbuf->InitDeepBuffer(800,800,1000);//初始化深度缓存器
for(int nFace=0;nFace<28;nFace++)//面循环
{
	if(nFace<26)
	{
		for(int nVertex=0;nVertex<F[nFace].vN;nVertex++)//顶点循环
		{
			PerProject(V[F[nFace].vI[nVertex]]);//透视投影
			Point[nVertex]=ScreenP;
			Point[nVertex].c=F[nFace].fClr;
		}
		zbuf->SetPoint(Point,4);//设置顶点
		zbuf->CreateBucket();//建立桶表
		zbuf->CreateEdge();//建立边表
		zbuf->Phong(pDC,ViewPoint,pLight,pMaterial);//颜色渐变填充三角形
		zbuf->ClearMemory();//内存清理
	}
	if(nFace==26||nFace==27)
	{
		for(int nVertex=0;nVertex<F[nFace].vN;nVertex++)//顶点循环
		{
			PerProject(V[F[nFace].vI[nVertex]]);//透视投影
			Point[nVertex]=ScreenP;
			Point[nVertex].c=F[nFace].fClr;
		}
		zbuf->SetPoint(Point,26);//设置顶点
		zbuf->CreateBucket();//建立桶表
		zbuf->CreateEdge();//建立边表
		zbuf->Phong(pDC,ViewPoint,pLight,pMaterial);//颜色渐变填充三角形
		zbuf->ClearMemory();//内存清理
	}
}
delete zbuf;//释放内存

}

第二个程序在第一个程序的基础上添加了纹理映射的功能,纹理模型分为多种。有函数纹理、图像纹理、几何纹理等。对应功能的实现则是添加相应的功能函数,然后在画图时进行调用即可。此处以几何纹理的映射为例。
先要在资源视图中的Bitmap文件夹中将你需要的几何纹理图片引入,其次在CMy1617417003klbView类中添加ReadBumpMap()函数。
void CMy1617417003klbView::ReadBumpMap()
{
CBitmap NewBitmap;
NewBitmap.LoadBitmap(IDB_BUMPTEXTURE);//导入DDB位图
NewBitmap.GetBitmap(&bmp);//将CBitmap的信息保存到Bitmap结构体中
int nbytesize=bmp.bmWidthBytesbmp.bmHeight;
im=new BYTE[nbytesize];
NewBitmap.GetBitmapBits(nbytesize,(LPVOID)im);
Image=new COLORREF
[bmp.bmHeight];
Imgx_Gradient=new double*[bmp.bmHeight];
Imgy_Gradient=new double*[bmp.bmHeight];
for(int n1=0;n1<bmp.bmHeight;n1++)
{
Image[n1]=new COLORREF[bmp.bmWidth];
Imgx_Gradient[n1]=new double[bmp.bmWidth];
Imgy_Gradient[n1]=new double[bmp.bmWidth];
}
for(n1=0;n1<bmp.bmHeight;n1++)
{
for(int n2=0;n2<bmp.bmWidth;n2++)
{
int pos=n1bmp.bmWidthBytes+4n2;//颜色分量位置
n1=bmp.bmHeight-1-n1;//位图从左下角向右上角绘制
Image[n1][n2]=RGB(im[pos+2],im[pos+1],im[pos]);
}
}
for(n1=0;n1<bmp.bmHeight;n1++)
{
for(int n2=0;n2<bmp.bmWidth;n2++)
{
int fontx,backx,fonty,backy;//一阶中心差分
fontx=n1+1;backx=n1-1;
fonty=n2+1;backy=n2-1;
//检测图片的边界,防止越界
if(backx<0)
backx=0;
if(backy<0)
backy=0;
if(fontx>bmp.bmHeight-1)
fontx=bmp.bmHeight-1;
if(fonty>bmp.bmWidth-1)
fonty=bmp.bmWidth-1;
//分别得到每个点的x与y的偏移量
Imgx_Gradient[n1][n2]=(GetRValue(Image[n1][fonty])-GetRValue(Image[n1][backy]));
Imgy_Gradient[n1][n2]=(GetRValue(Image[fontx][n2])-GetRValue(Image[backx][n2]));
}
}
delete []im;
}

void CMy1617417003klbView::DrawObject(CDC *pDC)
{

CPi3 Point[26];//透视投影后面的二维顶点数组

CZBuffer *zbuf=new CZBuffer;//申请内存
zbuf->InitDeepBuffer(800,800,1000);//初始化深度缓冲器
zbuf->ReadGradient(Imgx_Gradient,Imgy_Gradient);
CPi3 Point3[3];//底面与顶面三角形顶点数组
CT2 Texture3[3];//底面与顶面三角形纹理数组
CVector Normal3[3];//底面与顶面三角形法矢量数组
CPi3 Point4[4];//侧面四边形顶点数组
CT2 Texture4[4];//侧面四边形纹理数组
CVector Normal4[4];//侧面四边形法矢量数组
for(int i=0;i<N1;i++)
{
	for(int j=0;j<N2;j++)
	{			
		CVector ViewVector(V[F[i][j].vI[0]],ViewPoint);//面的视矢量
		ViewVector=ViewVector.Normalize();//单位化视矢量
		F[i][j].SetFaceNormal(V[F[i][j].vI[0]],V[F[i][j].vI[1]],V[F[i][j].vI[2]]);//计算小面片法矢量
		F[i][j].fNormal.Normalize();//单位化法矢量
		if(Dot(ViewVector,F[i][j].fNormal)>=0)
		{
			if(3==F[i][j].vN)//处理三角形面片
			{
				for(int m=0;m<F[i][j].vN;m++)
				{
					PerProject(V[F[i][j].vI[m]]);
					Point3[m]=ScreenP;
					Normal3[m]=CVector(V[F[i][j].vI[m]]);
				}
				double tempi=i+1,tempj=j+1;//对三角形面片进行特殊处理
				Texture3[0].u=gBeta*(j)/360.0*(bmp.bmWidth-1);Texture3[0].v=(1.0-gAlpha*i/180.0)*(bmp.bmHeight-1);  
				Texture3[1].u=gBeta*(j)/360.0*(bmp.bmWidth-1);Texture3[1].v=(1.0-gAlpha*tempi/180.0)*(bmp.bmHeight-1);
				Texture3[2].u=gBeta*tempj/360.0*(bmp.bmWidth-1);  Texture3[2].v=(1.0-gAlpha*i/180.0)*(bmp.bmHeight-1);
				zbuf->SetPoint(Point3,Normal3,Texture3,3);//初始化
				zbuf->CreateBucket();//创建桶表
				zbuf->CreateEdge();//创建边表
				zbuf->Phong1(pDC,ViewPoint,pLight,pMaterial);//填充三角形
				zbuf->ClearMemory();
			}
			else//处理四边形面片
			{
				for(int m=0;m<F[i][j].vN;m++)
				{
					PerProject(V[F[i][j].vI[m]]);
					Point4[m]=ScreenP;
					Normal4[m]=CVector(V[F[i][j].vI[m]]);
					Texture4[m]=T[F[i][j].vI[m]];
				}
				if(N2-1==j)//消除图像纹理的接缝
				{
					Texture4[2].u=bmp.bmWidth-1;
					Texture4[3].u=bmp.bmWidth-1;
				}
				zbuf->SetPoint(Point4,Normal4,Texture4,4);//初始化
				zbuf->CreateBucket();//创建桶表
				zbuf->CreateEdge();//创建边表
				zbuf->Phong(pDC,ViewPoint,pLight,pMaterial);//填充四边形
				zbuf->ClearMemory();
			}				
		}
	}
}
delete zbuf;

}

3.3相关数据结构的设计
在填充图形式设计一个桶表结构,此处具体见源代码相应类的创建。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值