MFC实现三维图像绘制(4)面的绘制

绘制物体的面时考虑两个问题:

1. 如何绘制?

2. 如何消隐不可见面?

针对这两个问题,分别采取有效边表算法和Z-Buffer缓冲器算法。

有效边表:

#include "Pi3.h"
#include "T2.h"

class CAET
{
public:
	CAET();
	virtual ~CAET();
public:
	double  x;//当前扫描线与有效边交点的x坐标
	int     yMax;//边的最大y值
	double  k;//斜率的倒数(x的增量)
	CPi3    ps;//边的起点
	CPi3    pe;//边的终点
	CAET   *pNext;
	CT2 ts;//起点纹理坐标
	CT2 te;//终点纹理坐标
};

桶表:

#include "AET.h"
class CBucket
{
public:
	CBucket();
	virtual ~CBucket();
public:
	int     ScanLine;     //扫描线
	CAET    *pET;         //桶上的边表指针
	CBucket *pNext;
};

具体绘制实现(这里涉及到物体的光照和纹理模型):

#include "Bucket.h"
#include "Vector3.h"
#include "Lighting.h"
#include "Pi3.h"
#include "Texture.h"
#include "T2.h"

class CFill
{
public:
	CFill(void);
	virtual ~CFill(void);
	void SetPoint(CPi3 *p, int, CT2*);//类的初始化
	void SetPoint(CPi3* p, int);//类的初始化
	void CreateBucket();//创建桶
	void CreateEdge();//边表
	void AddEt(CAET *);//合并ET表
	void EtOrder();//ET表排序
	void Gouraud(CDC *, int,CLighting, CMaterial*,CP3, CTexture*,BOOL);//填充多边形
	void Gouraud(CDC*, int, CLighting, CMaterial*, CP3);//填充多边形
	CRGB Interpolation(double, double, double, CRGB, CRGB);//颜色线性插值
	double Interpolation(double, double, double, double, double);//深度线性插值
	CVector3 Interpolation(double, double, double, CVector3, CVector3);//法向量线性插值
	CT2 Interpolation(double m, double m0, double m1, CT2 T0, CT2 T1);//纹理地址线性插值
	void ClearMemory();//清理内存
	void DeleteAETChain(CAET* pAET);//删除边表
	CRGB GetTextureColor(double u, double v, CTexture* pTexture);//获得纹理颜色
	void InitialDepthBuffer(int nWidth, int nHeight, double zDepth);//初始化深度缓冲器
public:
	int     PNum;//顶点个数
	CT2* T;//纹理动态数组
	CPi3    *P;//顶点坐标动态数组
	CAET    *pHeadE, *pCurrentE, *pEdge; //有效边表结点指针
	CBucket *pHeadB, *pCurrentB;        //桶表结点指针
	double** zBuffer;//深度缓冲器
	int nWidth, nHeight;//缓冲区宽度与高度
};
#include "pch.h"
#include "Fill.h"
#include "math.h"
#define ROUND(f) int(floor(f+0.5))//四舍五入宏定义

CFill::CFill(void)
{
	PNum = 0;
	P = NULL;
	pEdge = NULL;
	pHeadB = NULL;
	pHeadE = NULL;
}


CFill::~CFill(void)
{
	if (P != NULL)
	{
		delete[] P;
		P = NULL;
	}
	for (int i = 0;i < nWidth;i++)
	{
		delete[] zBuffer[i];
		zBuffer[i] = NULL;
	}
	if (zBuffer != NULL)
	{
		delete zBuffer;
		zBuffer = NULL;
	}
	ClearMemory();
}

void CFill::SetPoint(CPi3 *p, int m, CT2* t)
{
	if (P != NULL)
	{
		delete[] P;
		P = NULL;
	}
	P = new CPi3[m];//创建一维动态数组
	T = new CT2[m];
	for (int i = 0; i < m; i++)
	{
		P[i] = p[i];
		P[i].y = ROUND(P[i].y);
		T[i] = t[i];
	}
	PNum = m;
}

void CFill::SetPoint(CPi3* p, int m)
{
	if (P != NULL)
	{
		delete[] P;
		P = NULL;
	}
	P = new CPi3[m];//创建一维动态数组
	for (int i = 0; i < m; i++)
	{
		P[i] = p[i];
		P[i].y = ROUND(P[i].y);
	}
	PNum = m;
}

void CFill::CreateBucket()//创建桶表
{
	int yMin, yMax;
	yMin = yMax = P[0].y;
	for (int i = 0; i < PNum; i++)//查找多边形所覆盖的最小和最大扫描线
	{
		if (P[i].y < yMin)
		{
			yMin = P[i].y;//扫描线的最小值
		}
		if (P[i].y > yMax)
		{
			yMax = P[i].y;//扫描线的最大值
		}
	}
	for (int y = yMin; y <= yMax; y++)
	{
		if (yMin == y)//如果是扫描线的最小值
		{
			pHeadB = new CBucket;//建立桶的头结点
			pCurrentB = pHeadB;//pCurrentB为CBucket当前结点指针
			pCurrentB->ScanLine = yMin;
			pCurrentB->pET = NULL;//没有链接边表
			pCurrentB->pNext = NULL;
		}
		else//其他扫描线
		{
			pCurrentB->pNext = new CBucket;//建立桶的其他结点
			pCurrentB = pCurrentB->pNext;
			pCurrentB->ScanLine = y;
			pCurrentB->pET = NULL;
			pCurrentB->pNext = NULL;
		}
	}
}

void CFill::CreateEdge()//创建边表
{
	for (int i = 0; i < PNum; i++)
	{
		pCurrentB = pHeadB;
		int j = (i + 1) % PNum;//边的第2个顶点,P[i]和P[j]点对构成边
		if (P[i].y < P[j].y)//边的终点比起点高
		{
			pEdge = new CAET;
			pEdge->x = P[i].x;//计算ET表的值
			pEdge->yMax = P[j].y;
			pEdge->k = (P[j].x - P[i].x) / (P[j].y - P[i].y);//代表1/k
			pEdge->ps = P[i];//绑定顶点和颜色
			pEdge->pe = P[j];
			pEdge->ts= T[i];//绑定纹理	
			pEdge->te = T[j];
			pEdge->pNext = NULL;
			while (pCurrentB->ScanLine != P[i].y)//在桶内寻找当前边的yMin
			{
				pCurrentB = pCurrentB->pNext;//移到yMin所在的桶结点
			}
		}
		if (P[j].y < P[i].y)//边的终点比起点低
		{
			pEdge = new CAET;
			pEdge->x = P[j].x;
			pEdge->yMax = P[i].y;
			pEdge->k = (P[i].x - P[j].x) / (P[i].y - P[j].y);
			pEdge->ps = P[i];
			pEdge->pe = P[j];
			pEdge->ts = T[i];//绑定纹理	
			pEdge->te = T[j];
			pEdge->pNext = NULL;
			while (pCurrentB->ScanLine != P[j].y)
			{
				pCurrentB = pCurrentB->pNext;
			}
		}
		if (P[i].y != P[j].y)
		{
			pCurrentE = pCurrentB->pET;
			if (pCurrentE == NULL)
			{
				pCurrentE = pEdge;
				pCurrentB->pET = pCurrentE;
			}
			else
			{
				while (pCurrentE->pNext != NULL)
				{
					pCurrentE = pCurrentE->pNext;
				}
				pCurrentE->pNext = pEdge;
			}
		}
	}
}

void CFill::Gouraud(CDC *pDC, int light_type,CLighting light, CMaterial* material, CP3 EyePoint, CTexture* pTexture,BOOL IfBump)//填充多边形
{
	CAET *pT1 = NULL, *pT2 = NULL;
	pHeadE = NULL;
	for (pCurrentB = pHeadB; pCurrentB != NULL; pCurrentB = pCurrentB->pNext)
	{
		for (pCurrentE = pCurrentB->pET; pCurrentE != NULL; pCurrentE = pCurrentE->pNext)
		{
			pEdge = new CAET;
			pEdge->x = pCurrentE->x;
			pEdge->yMax = pCurrentE->yMax;
			pEdge->k = pCurrentE->k;
			pEdge->ps = pCurrentE->ps;
			pEdge->pe = pCurrentE->pe;
			pEdge->pNext = NULL;
			pEdge->ts = pCurrentE->ts;
			pEdge->te = pCurrentE->te;
			pEdge->pNext = NULL;
			AddEt(pEdge);
		}
		EtOrder();
		pT1 = pHeadE;
		if (pT1 == NULL)
		{
			return;
		}
		while (pCurrentB->ScanLine >= pT1->yMax)//下闭上开
		{
			CAET * pAETTEmp = pT1;
			pT1 = pT1->pNext;
			delete pAETTEmp;
			pHeadE = pT1;
			if (pHeadE == NULL)
				return;
		}
		if (pT1->pNext != NULL)
		{
			pT2 = pT1;
			pT1 = pT2->pNext;
		}
		while (pT1 != NULL)
		{
			if (pCurrentB->ScanLine >= pT1->yMax)//下闭上开
			{
				CAET* pAETTemp = pT1;
				pT2->pNext = pT1->pNext;
				pT1 = pT2->pNext;
				delete pAETTemp;
			}
			else
			{
				pT2 = pT1;
				pT1 = pT2->pNext;
			}
		}
		CRGB Ca, Cb, Cf;//Ca、Cb代表边上任意点的颜色,Cf代表面上任意点的颜色
		double za, zb, zf;
		CVector3 va, vb, vf;
		CT2 ta, tb, tf;//ta和tb代表边上任意点的纹理地址,tf代表面上任意点的纹理地址
		ta = Interpolation(pCurrentB->ScanLine, pHeadE->ps.y, pHeadE->pe.y, pHeadE->ts, pHeadE->te);
		tb = Interpolation(pCurrentB->ScanLine, pHeadE->pNext->ps.y, pHeadE->pNext->pe.y, pHeadE->pNext->ts, pHeadE->pNext->te);
		Ca = Interpolation(pCurrentB->ScanLine, pHeadE->ps.y, pHeadE->pe.y, pHeadE->ps.c, pHeadE->pe.c);
		Cb = Interpolation(pCurrentB->ScanLine, pHeadE->pNext->ps.y, pHeadE->pNext->pe.y, pHeadE->pNext->ps.c, pHeadE->pNext->pe.c);
		za = Interpolation(pCurrentB->ScanLine, pHeadE->ps.y, pHeadE->pe.y, pHeadE->ps.z, pHeadE->pe.z);
		zb = Interpolation(pCurrentB->ScanLine, pHeadE->pNext->ps.y, pHeadE->pNext->pe.y, pHeadE->pNext->ps.z, pHeadE->pNext->pe.z);
		va = Interpolation(pCurrentB->ScanLine, pHeadE->ps.y, pHeadE->pe.y, pHeadE->ps.v, pHeadE->pe.v);
		vb = Interpolation(pCurrentB->ScanLine, pHeadE->pNext->ps.y, pHeadE->pNext->pe.y, pHeadE->pNext->ps.v, pHeadE->pNext->pe.v);
		BOOL Flag = FALSE;
		double xb, xe;//扫描线和有效边相交区间的起点和终点坐标
		for (pT1 = pHeadE; pT1 != NULL; pT1 = pT1->pNext)
		{
			if (Flag == FALSE)
			{
				xb = pT1->x;
				Flag = TRUE;
			}
			else
			{
				xe = pT1->x;
				for (double x = xb; x < xe; x++)//左闭右开
				{
					if (ROUND(x) + nWidth / 2 < nWidth && ROUND(x) + nWidth / 2 > 0 && pCurrentB->ScanLine + nHeight / 2 < nHeight && pCurrentB->ScanLine + nHeight / 2 > 0) {
						zf = Interpolation(x, xb, xe, za, zb);
						if (zf >= zBuffer[ROUND(x) + nWidth / 2][pCurrentB->ScanLine + nHeight / 2])//如果当前采样点的深度小于帧缓冲器中原采样点的深度)
						{
							tf = Interpolation(x, xb, xe, ta, tb);
							tf.c = GetTextureColor(ROUND(tf.u), ROUND(tf.v), pTexture);
							material->SetDiffuse(tf.c);//用纹理颜色作为材质的漫反射光反射率
							material->SetAmbient(tf.c);//用纹理颜色作为材质的环境光反射率
							if (light_type == 1) {
								Cf = Interpolation(x, xb, xe, Ca, Cb);
							}
							else if (light_type == 2) {
								vf = Interpolation(x, xb, xe, va, vb);
								// 凹凸纹理
								if (IfBump) {
									CRGB frontU = GetTextureColor(ROUND(tf.u - 1), ROUND(tf.v), pTexture);
									CRGB backU = GetTextureColor(ROUND(tf.u + 1), ROUND(tf.v), pTexture);
									double Bu = (frontU.blue - backU.blue) / 2;
									CRGB frontV = GetTextureColor(ROUND(tf.u), ROUND(tf.v - 1), pTexture);
									CRGB backV = GetTextureColor(ROUND(tf.u), ROUND(tf.v + 1), pTexture);
									double Bv = (frontV.blue - backV.blue) / 2;
									CVector3 DNormal = CVector3(Bu, Bv, 0);
									double BumpScale = 100.0;
									vf = vf + BumpScale * DNormal;
									vf = vf.Normalize();
								}
								Cf = light.Illuminate(EyePoint, CP3(ROUND(x), pCurrentB->ScanLine, zf), vf, material);
							}
							zBuffer[ROUND(x) + nWidth / 2][pCurrentB->ScanLine + nHeight / 2] = zf;//使用当前采样点的深度更新深度缓冲器
							pDC->SetPixelV(ROUND(x), pCurrentB->ScanLine, RGB(Cf.red * 255, Cf.green * 255, Cf.blue * 255));
						}
					}
				}
				Flag = FALSE;
			}
		}
		for (pT1 = pHeadE; pT1 != NULL; pT1 = pT1->pNext)//边的连续性
		{
			pT1->x = pT1->x + pT1->k;
		}
	}
}

void CFill::AddEt(CAET *pNewEdge)//合并ET表
{
	CAET *pCE = pHeadE;
	if (pCE == NULL)
	{
		pHeadE = pNewEdge;
		pCE = pHeadE;
	}
	else
	{
		while (pCE->pNext != NULL)
		{
			pCE = pCE->pNext;
		}
		pCE->pNext = pNewEdge;
	}
}
void CFill::EtOrder()//边表的冒泡排序算法
{
	CAET *pT1 = NULL, *pT2 = NULL;
	int Count = 1;
	pT1 = pHeadE;
	if (NULL == pT1)
	{
		return;
	}
	if (NULL == pT1->pNext)//如果该ET表没有再连ET表
	{
		return;//桶结点只有一条边,不需要排序
	}
	while (NULL != pT1->pNext)//统计结点的个数
	{
		Count++;
		pT1 = pT1->pNext;
	}
	for (int i = 1; i < Count; i++)//冒泡排序
	{
		pT1 = pHeadE;
		if (pT1->x > pT1->pNext->x)//按x由小到大排序
		{
			pT2 = pT1->pNext;
			pT1->pNext = pT1->pNext->pNext;
			pT2->pNext = pT1;
			pHeadE = pT2;
		}
		else
		{
			if (pT1->x == pT1->pNext->x)
			{
				if (pT1->k > pT1->pNext->k)//按斜率由小到大排序
				{
					pT2 = pT1->pNext;
					pT1->pNext = pT1->pNext->pNext;
					pT2->pNext = pT1;
					pHeadE = pT2;
				}
			}
		}
		pT1 = pHeadE;
		while (pT1->pNext->pNext != NULL)
		{
			pT2 = pT1;
			pT1 = pT1->pNext;
			if (pT1->x > pT1->pNext->x)//按x由小到大排序
			{
				pT2->pNext = pT1->pNext;
				pT1->pNext = pT1->pNext->pNext;
				pT2->pNext->pNext = pT1;
				pT1 = pT2->pNext;
			}
			else
			{
				if (pT1->x == pT1->pNext->x)
				{
					if (pT1->k > pT1->pNext->k)//按斜率由小到大排序
					{
						pT2->pNext = pT1->pNext;
						pT1->pNext = pT1->pNext->pNext;
						pT2->pNext->pNext = pT1;
						pT1 = pT2->pNext;
					}
				}
			}
		}
	}
}

CRGB CFill::GetTextureColor(double u, double v, CTexture* pTexture)//获得纹理颜色
{
	/*检测图片的边界,防止越界*/
	if (u < 0) u = 0; if (v < 0) v = 0;
	if (u > pTexture->bmp.bmWidth - 1) 	u = pTexture->bmp.bmWidth - 1;
	if (v > pTexture->bmp.bmHeight - 1)	v = pTexture->bmp.bmHeight - 1;
	/*查找对应纹理空间的颜色值*/
	v = pTexture->bmp.bmHeight - 1 - v;
	int position = ROUND(v * pTexture->bmp.bmWidthBytes + 4 * u);//颜色分量位置
	COLORREF color = RGB(pTexture->image[position + 2], pTexture->image[position + 1], pTexture->image[position]);//获取颜色值
	return  CRGB(GetRValue(color) / 255.0, GetGValue(color) / 255.0, GetBValue(color) / 255.0);
}

CRGB CFill::Interpolation(double t, double t1, double t2, CRGB c1, CRGB c2)//颜色线性插值
{
	CRGB c;
	c = (t - t2) / (t1 - t2)*c1 + (t - t1) / (t2 - t1)*c2;
	return c;
}

double CFill::Interpolation(double t, double t1, double t2, double z1,double z2)//线性插值
{
	double z;
	z = (t - t2) / (t1 - t2) * z1 + (t - t1) / (t2 - t1) * z2;
	return z;
}

CVector3 CFill::Interpolation(double x, double xa, double xb, CVector3 va, CVector3 vb)//法向量线性插值
{
	CVector3 v;
	v = (x - xb) / (xa - xb) * va + (x - xa) / (xb - xa) * vb;
	return v;
}

CT2 CFill::Interpolation(double m, double m0, double m1, CT2 T0, CT2 T1)//纹理地址线性插值
{
	CT2 Texture;
	Texture = (m1 - m) / (m1 - m0) * T0 + (m - m0) / (m1 - m0) * T1;
	return Texture;
}

void CFill::ClearMemory()//安全删除所有桶和桶上面的边
{
	DeleteAETChain(pHeadE);
	CBucket *pBucket = pHeadB;
	while (pBucket != NULL)// 针对每一个桶
	{
		CBucket * pBucketTemp = pBucket->pNext;
		DeleteAETChain(pBucket->pET);
		delete pBucket;
		pBucket = pBucketTemp;
	}
	pHeadB = NULL;
	pHeadE = NULL;
}

void CFill::DeleteAETChain(CAET* pAET)
{
	while (pAET != NULL)
	{
		CAET* pAETTemp = pAET->pNext;
		delete pAET;
		pAET = pAETTemp;
	}
}

void CFill::InitialDepthBuffer(int nWidth, int nHeight, double zDepth)//初始化深度缓冲
{
	this->nWidth = nWidth, this->nHeight = nHeight;
	zBuffer = new double* [nWidth];
	for (int i = 0;i < nWidth;i++)
		zBuffer[i] = new double[nHeight];
	for (int i = 0;i < nWidth;i++)//初始化深度缓冲
		for (int j = 0;j < nHeight;j++)
			zBuffer[i][j] = zDepth;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值