制作金材质的圆环GOURAUD光照模型

一、实验项目要求

1. 建立立方体类。

2. 背面剔除算法消隐。

二、理论分析或算法分析

1.建立立方体类:

在实验中,建立一个Cube类,在Cube类中定义立方体的八个顶点,六个面,为了可以方便后面绘制面,所以定义了一个Facet类,用来定义表面顶点和面的顶点索引号,为了绘制出这个立方体,在立方体类中,需要定义这八个顶点的坐标(void CCube::ReadPoint(void)),然后读入面表(void CCube::ReadFacet(void)//面表),在这个类中定义Draw函数(void CCube::Draw(CDC* pDC))用来绘制出立方体,在这个里面,利用循环来绘制出一个面(for (int nPoint = 0; nPoint < F[nFacet].Number; nPoint++)//顶点循环),然后在循环来绘制出六个面(for(int nFacet = 0; nFacet < 6; nFacet++)//面循环);

2.背面剔除算法消隐:

这个算法是针对凸多边形而设计的,其表面要么完全可见,要么不可见。背面剔除算法的的关键是给出测试其每个表面可见性的判别式,可以根据其外法向量N与视向量V(从表面上的一个顶点指向视点)的夹角@来进行可见性检测。在这个算法中,主要用到的就是三维向量类Vectors3,主要成员函数为点乘(数量积)函数和叉乘(向量积)函数。使用背面剔除算法对立方体进行消隐,主要是对条件的判定(if (DotProduct(ViewVector, FacetNormal)>=0)//背面剔除),满足此条件,就可以看见,从而达到了消隐的目的;        

三、实现方法

1.建立立方体类:

Cube.h:

#pragma once
#include"Projection.h"
#include"Facet.h"
#include"Triangle.h"
#include"Vector3.h"
#include"Lighting.h"

class CCube
{
public:
	CCube(void);
	virtual ~CCube(void);
	CP3* GetVertexArrayName(void);//获得数组名
	void ReadPoint(void);//读入点表
	void ReadFacet(void);//读入面表	
	void SetScene(CLighting* pLight, CMaterial* pMaterial);//设置场景
	void Draw(CDC* pDC);//绘制图形
private:
	CP3 P[8];//点表
	CFacet F[6];//面表
CProjection projection;//投影
	CLighting* pLight;//光照
CMaterial* pMaterial;//材质
};

Cube.cpp:

#include "pch.h"
#include "Cube.h"
#include <math.h>//包含数学头文件

CCube::CCube(void)
{
}

CCube::~CCube(void)
{

}

CP3* CCube::GetVertexArrayName(void)//获得数组名
{
	return P;
}

void CCube::SetScene(CLighting* pLight, CMaterial* pMaterial)//设置场景
{
	this->pLight = pLight;
	this->pMaterial = pMaterial;
}


void CCube::ReadPoint(void)//点表
{
	P[0].x = 0, P[0].y = 0, P[0].z = 0;
	P[1].x = 1, P[1].y = 0, P[1].z = 0;
	P[2].x = 1, P[2].y = 1, P[2].z = 0;
	P[3].x = 0, P[3].y = 1, P[3].z = 0;
	P[4].x = 0, P[4].y = 0, P[4].z = 1;
	P[5].x = 1, P[5].y = 0, P[5].z = 1;
	P[6].x = 1, P[6].y = 1, P[6].z = 1;
	P[7].x = 0, P[7].y = 1, P[7].z = 1;
}



void CCube::ReadFacet(void)//面表
{
	F[0].Number = 4;F[0].Index[0] = 4;F[0].Index[1] = 5;F[0].Index[2] = 6;F[0].Index[3] = 7;//前面
	F[1].Number = 4;F[1].Index[0] = 0;F[1].Index[1] = 3;F[1].Index[2] = 2;F[1].Index[3] = 1;//后面
	F[2].Number = 4;F[2].Index[0] = 0;F[2].Index[1] = 4;F[2].Index[2] = 7;F[2].Index[3] = 3;//左面
	F[3].Number = 4;F[3].Index[0] = 1;F[3].Index[1] = 2;F[3].Index[2] = 6;F[3].Index[3] = 5;//右面
	F[4].Number = 4;F[4].Index[0] = 2;F[4].Index[1] = 3;F[4].Index[2] = 7;F[4].Index[3] = 6;//顶面
	F[5].Number = 4;F[5].Index[0] = 0;F[5].Index[1] = 1;F[5].Index[2] = 5;F[5].Index[3] = 4;//底面
}


void CCube::Draw(CDC* pDC)
{
	CP3 ScreenPoint[4];//三维屏幕点
	CTriangle* pFill = new CTriangle;   //申请内存
	for(int nFacet = 0; nFacet < 6; nFacet++)//面循环
	{
		CP3 ViewPoint = projection.GetEye();//视点
		CVector3 ViewVector(P[F[nFacet].Index[0]], ViewPoint);// 面的视向量
		ViewVector = ViewVector.Normalize();//视向量单位化
		CVector3 Vector01(P[F[nFacet].Index[0]], P[F[nFacet].Index[1]]), Vector02(P[F[nFacet].Index[0]], P[F[nFacet].Index[2]]);//边向量
		CVector3 FacetNormal = CrossProduct(Vector01, Vector02);//面法向量
		FacetNormal = FacetNormal.Normalize();
		if (DotProduct(ViewVector, FacetNormal)>=0)//背面剔除
		{

			for (int nPoint = 0; nPoint < F[nFacet].Number; nPoint++)//顶点循环
				ScreenPoint[nPoint] = projection.PerspectiveProjection3(P[F[nFacet].Index[nPoint]]);
			pFill->SetPoint(ScreenPoint[0], ScreenPoint[2], ScreenPoint[3]);
			pFill->GouraudShader(pDC);
			pFill->SetPoint(ScreenPoint[0], ScreenPoint[1], ScreenPoint[2]);
			pFill->GouraudShader(pDC);
		}
	}
}

2.背面剔除消隐算法:

Vectors3.h:

#pragma once
#include "P3.h"

class CVector3
{
public:
	CVector3(void);
	virtual ~CVector3(void);
	CVector3(double x, double y, double z);//绝对向量
	CVector3(const CP3 &vertex);
	CVector3(const CP3 &Vertex0, const CP3 &Vertex1);//相对向量
	double Magnitude(void);//计算向量的模
	CVector3 Normalize(void);//规范化向量
	friend CVector3 operator + (const CVector3 &v0, const CVector3 &v1);//运算符重载
	friend CVector3 operator - (const CVector3 &v0, const CVector3 &v1);
	friend CVector3 operator * (const CVector3 &v, double scalar);
	friend CVector3 operator * (double scalar, const CVector3 &v);
	friend CVector3 operator / (const CVector3 &v, double scalar);
	friend double DotProduct(const CVector3 &v0, const CVector3 &v1);//计算向量的点积
	friend CVector3 CrossProduct(const CVector3 &v0, const CVector3 &v1);//计算向量的叉积
private:
	double x, y, z;
};

Vectors3.cpp:

#include "pch.h"
#include "Vector3.h"

CVector3::CVector3(void)
{
	x = 0.0, y = 0.0, z = 1.0;//指向z轴正向
}


CVector3::~CVector3(void)
{
}

CVector3::CVector3(double x, double y, double z)//绝对向量
{
	this->x = x;
	this->y = y;
	this->z = z;
}

CVector3::CVector3(const CP3 &vertex)
{
	x = vertex.x;
	y = vertex.y;
	z = vertex.z;
}

CVector3::CVector3(const CP3 &Vertex0, const CP3 &Vertex1)//相对向量
{
	x = Vertex1.x - Vertex0.x;
	y = Vertex1.y - Vertex0.y;
	z = Vertex1.z - Vertex0.z;
}

double CVector3::Magnitude(void)//向量的模
{
	return sqrt(x * x + y * y + z * z);
}

CVector3 CVector3::Normalize(void)//规范化为单位向量
{
	CVector3 vector;
	double magnitude = sqrt(x * x + y * y + z * z);
	if (fabs(magnitude) < 1e-4)
		magnitude = 1.0;
	vector.x = x / magnitude;
	vector.y = y / magnitude;
	vector.z = z / magnitude;
	return vector;
}

CVector3 operator + (const CVector3 &v0, const CVector3 &v1)//向量的和
{
	CVector3 vector;
	vector.x = v0.x + v1.x;
	vector.y = v0.y + v1.y;
	vector.z = v0.z + v1.z;
	return vector;
}

CVector3 operator - (const CVector3 &v0, const CVector3 &v1)//向量的差
{
	CVector3 vector;
	vector.x = v0.x - v1.x;
	vector.y = v0.y - v1.y;
	vector.z = v0.z - v1.z;
	return vector;
}

CVector3 operator * (const CVector3 &v, double scalar)//向量与常量的积
{
	CVector3 vector;
	vector.x = v.x * scalar;
	vector.y = v.y * scalar;
	vector.z = v.z * scalar;
	return vector;
}

CVector3 operator * (double scalar, const CVector3 &v)//常量与向量的积
{
	CVector3 vector;
	vector.x = v.x * scalar;
	vector.y = v.y * scalar;
	vector.z = v.z * scalar;
	return vector;
}

CVector3 operator / (const CVector3 &v, double scalar)//向量数除
{
	if (fabs(scalar) < 1e-4)
		scalar = 1.0;
	CVector3 vector;
	vector.x = v.x / scalar;
	vector.y = v.y / scalar;
	vector.z = v.z / scalar;
	return vector;
}

double DotProduct(const CVector3 &v0, const CVector3 &v1)//向量的点积
{
	return(v0.x * v1.x + v0.y * v1.y + v0.z * v1.z);
}

CVector3 CrossProduct(const CVector3 &v0, const CVector3 &v1)//向量的叉积
{
	CVector3 vector;
	vector.x = v0.y * v1.z - v0.z * v1.y;
	vector.y = v0.z * v1.x - v0.x * v1.z;
	vector.z = v0.x * v1.y - v0.y * v1.x;
	return vector;
}

投影方式:

Projection.h:

#pragma once
#include "P3.h"

class CProjection
{
public:
	CProjection(void);
	virtual ~CProjection(void);
	void SetEye(double R);//设置视径
	CP3 GetEye(void);//读取视点
	CP2 ObliqueProjection(CP3 WorldPoint);//斜投影
	CP2 OrthogonalProjection(CP3 WorldPoint);//正交投影
	CP2 PerspectiveProjection2(CP3 WorldPoint);//二维透视投影
	CP3 PerspectiveProjection3(CP3 WorldPoint);//三维透视投影
private:
	CP3 EyePoint;//视点
	double R, d;//视径和视距
};
Projection.cpp:
#include "pch.h"
#include "Projection.h"

CProjection::CProjection(void)
{
	R = 1200, d = 800;
	EyePoint.x = 0, EyePoint.y = 0, EyePoint.z = R;//视点位于屏幕正前方
}

CProjection::~CProjection(void)
{
}

void CProjection::SetEye(double R)//设置视径
{
	EyePoint.z = R;
}

CP3 CProjection::GetEye(void)//读取视点
{
	return EyePoint;
}


CP2 CProjection::ObliqueProjection(CP3 WorldPoint)//斜二测投影
{
	CP2 ScreenPoint;//屏幕坐标系二维点
    ScreenPoint.x = WorldPoint.x - 0.3536 * WorldPoint.z;
	ScreenPoint.y = WorldPoint.y - 0.3536 * WorldPoint.z;
	return ScreenPoint;
}

CP2 CProjection::OrthogonalProjection(CP3 WorldPoint)//正交投影
{
	CP2 ScreenPoint;//屏幕坐标系二维点
	ScreenPoint.x = WorldPoint.x;
	ScreenPoint.y = WorldPoint.y;
	return ScreenPoint;
}

CP2 CProjection::PerspectiveProjection2(CP3 WorldPoint)
{
	CP3 ViewPoint;//观察坐标系三维点
	ViewPoint.x = WorldPoint.x;
	ViewPoint.y = WorldPoint.y;
	ViewPoint.z = EyePoint.z - WorldPoint.z;
	ViewPoint.c = WorldPoint.c;
	CP2 ScreenPoint;//屏幕坐标系二维点
	ScreenPoint.x = d * ViewPoint.x / ViewPoint.z;
	ScreenPoint.y = d * ViewPoint.y / ViewPoint.z;
	ScreenPoint.c = ViewPoint.c;
	return ScreenPoint;
}
   
CP3 CProjection::PerspectiveProjection3(CP3 WorldPoint)
{
	CP3 ViewPoint;//观察坐标系三维点
	ViewPoint.x = WorldPoint.x; 
	ViewPoint.y = WorldPoint.y;
	ViewPoint.z = EyePoint.z - WorldPoint.z;
	ViewPoint.c = WorldPoint.c;
	CP3 ScreenPoint;//屏幕坐标系三维点
	ScreenPoint.x = d * ViewPoint.x / ViewPoint.z;
	ScreenPoint.y = d * ViewPoint.y / ViewPoint.z;
	ScreenPoint.z = (ViewPoint.z - d) * d / ViewPoint.z;//Bouknight公式
	ScreenPoint.c = ViewPoint.c;
	return ScreenPoint;
}

在TestView.h里面定义:

protected:
	CCube cube;
	double Alpha, Beta;
	CTransform3 transform;
	BOOL bPlay;//动画按钮
	int	nLightSourceNumber;//光源数量
	CLighting* pLight;//光照环境
	CMaterial* pMaterial;//物体材质
public:
	void DoubleBuffer(CDC* pDC);//双缓冲
	void DrawObject(CDC* pDC);//绘制图形
	void InitializeLightingScene(void);//初始化光照场景

在TestView.cpp里面定义:

// TestView.cpp: CTestView 类的实现
//

#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "Test.h"
#endif

#include "TestDoc.h"
#include "TestView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CTestView

IMPLEMENT_DYNCREATE(CTestView, CView)

BEGIN_MESSAGE_MAP(CTestView, CView)
	// 标准打印命令
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
	ON_COMMAND(ID_GRAPH_ANIMATION, &CTestView::OnGraphAnimation)
	ON_UPDATE_COMMAND_UI(ID_GRAPH_ANIMATION, &CTestView::OnUpdateGraphAnimation)
	ON_WM_TIMER()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()

// CTestView 构造/析构

CTestView::CTestView() noexcept
{
	// TODO: 在此处添加构造代码
	bPlay = FALSE;
	double nEdge = 400;
	cube.ReadPoint();
	cube.ReadFacet();
	transform.SetMatrix(cube.GetVertexArrayName(), 8);
	transform.Scale(nEdge, nEdge, nEdge);
	transform.Translate(-nEdge / 2, -nEdge / 2, -nEdge / 2);//平移变换
	InitializeLightingScene();
	cube.SetScene(pLight, pMaterial);//设置场景
}

CTestView::~CTestView()
{
	if (pLight != NULL)
	{
		delete pLight;
		pLight = NULL;
	}
	if (pMaterial != NULL)
	{
		delete pMaterial;
		pMaterial = NULL;
	}
}

void CTestView::InitializeLightingScene(void)//初始化光照环境
{
	//设置光源属性
	nLightSourceNumber = 1;//光源个数
	pLight = new CLighting(nLightSourceNumber);//一维光源动态数组
	pLight->LightSource[0].SetPosition(0, 0, 500);//设置光源位置坐标
	for (int i = 0; i < nLightSourceNumber; i++)
	{
		pLight->LightSource[i].L_Diffuse = CRGB(1.0, 1.0, 1.0);//光源的漫反射颜色
		pLight->LightSource[i].L_Specular = CRGB(1.0, 1.0, 1.0);//光源镜面高光颜色
		pLight->LightSource[i].L_C0 = 1.0;//常数衰减因子
		pLight->LightSource[i].L_C1 = 0.0000001;//线性衰减因子
		pLight->LightSource[i].L_C2 = 0.00000001;//二次衰减因子
		pLight->LightSource[i].L_OnOff = TRUE;//光源开启
	}
	//设置材质属性
	pMaterial = new CMaterial;
	pMaterial->SetAmbient(CRGB(0.247, 0.200, 0.075));//环境反射率
	pMaterial->SetDiffuse(CRGB(0.752, 0.606, 0.226));//漫反射率
	pMaterial->SetSpecular(CRGB(0.628, 0.556, 0.366));//镜面反射率
	pMaterial->SetEmission(CRGB(0.0, 0.0, 0.0));//自身辐射的颜色
	pMaterial->SetExponent(50);//高光指数
}

BOOL CTestView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: 在此处通过修改
	//  CREATESTRUCT cs 来修改窗口类或样式

	return CView::PreCreateWindow(cs);
}

// CTestView 绘图

void CTestView::OnDraw(CDC* pDC)
{
	CTestDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	DoubleBuffer(pDC);
}

void CTestView::DoubleBuffer(CDC* pDC)//双缓冲
{
	CRect rect;//定义客户区矩形
	GetClientRect(&rect);//获得客户区的大小
	pDC->SetMapMode(MM_ANISOTROPIC);//pDC自定义坐标系
	pDC->SetWindowExt(rect.Width(), rect.Height());//设置窗口范围
	pDC->SetViewportExt(rect.Width(), -rect.Height());//设置视区范围,x轴水平向右,y轴垂直向上
	pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);//客户区中心为原点
	CDC memDC;//内存DC
	memDC.CreateCompatibleDC(pDC);//创建一个与显示pDC兼容的内存memDC
	CBitmap NewBitmap, *pOldBitmap;//内存中承载的临时位图 
	NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());//创建兼容位图 
	pOldBitmap = memDC.SelectObject(&NewBitmap);//将兼容位图选入memDC 
	//memDC.FillSolidRect(rect, pDC->GetBkColor());//按原来背景填充客户区,否则是黑色
	memDC.SetMapMode(MM_ANISOTROPIC);//memDC自定义坐标系
	memDC.SetWindowExt(rect.Width(), rect.Height());
	memDC.SetViewportExt(rect.Width(), -rect.Height());
	memDC.SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
	rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);
	DrawObject(&memDC);//向memDC绘制图形
	pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, -rect.Width() / 2, -rect.Height() / 2, SRCCOPY);//将内存memDC中的位图拷贝到显示pDC中
	memDC.SelectObject(pOldBitmap);//恢复位图
	NewBitmap.DeleteObject();//删除位图
}

void CTestView::DrawObject(CDC* pDC)//绘制图形
{
	cube.Draw(pDC);
}

// CTestView 打印

BOOL CTestView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// 默认准备
	return DoPreparePrinting(pInfo);
}

void CTestView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 添加额外的打印前进行的初始化过程
}

void CTestView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 添加打印后进行的清理过程
}


// CTestView 诊断

#ifdef _DEBUG
void CTestView::AssertValid() const
{
	CView::AssertValid();
}

void CTestView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CTestDoc* CTestView::GetDocument() const // 非调试版本是内联的
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTestDoc)));
	return (CTestDoc*)m_pDocument;
}
#endif //_DEBUG


// CTestView 消息处理程序


void CTestView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	switch (nChar)
	{
	case VK_UP:
		Alpha = -5;
		transform.RotateX(Alpha);
		break;
	case VK_DOWN:
		Alpha = +5;
		transform.RotateX(Alpha);
		break;
	case VK_LEFT:
		Beta = -5;
		transform.RotateY(Beta);
		break;
	case VK_RIGHT:
		Beta = +5;
		transform.RotateY(Beta);
		break;
	default:
		break;
	}
	Invalidate(FALSE);
	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CTestView::OnGraphAnimation()
{
	// TODO: 在此添加命令处理程序代码
	bPlay = !bPlay;
	if (bPlay)//设置定时器
		SetTimer(1, 150, NULL);
	else
		KillTimer(1);
}

void CTestView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	Alpha = 5, Beta = 5;
	transform.RotateX(Alpha);
	transform.RotateY(Beta);
	Invalidate(FALSE);
	CView::OnTimer(nIDEvent);
}

void CTestView::OnUpdateGraphAnimation(CCmdUI *pCmdUI)
{
	// TODO: 在此添加命令更新用户界面处理程序代码
	if (bPlay)
		pCmdUI->SetCheck(TRUE);
	else
		pCmdUI->SetCheck(FALSE);
}

四、实验结果分析

实验结果

实验总结

在这个实验中,刚开始的时候以为是自己设计的问题,后来找问题的时候才知道是光源坐标的问题,刚开始离得太近,但是就好像没有光源一样,所以找到了光源的问题,最后得以解决。

实验帮助

在同学的帮助下,刚开始把光源那块插进去,可以有光源了。

每次在进行实验时,不要一直自己在那里想,要在同学的帮助下,进行帮助。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值