一、实验项目要求
1. 建立立方体类。
2. 背面剔除算法消隐。
3.PhongShader光照。
4.使用六张纹理图片。
二、理论分析或算法分析
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)//背面剔除),满足此条件,就可以看见,从而达到了消隐的目的;
3.PhongShader光照:
这是一个着色算法,将一个四边形网格分为上三角形和下三角形,使用双线性插值来进行对像素点的着色,首先确定三角形类型,判断是属于“左三角形”还是属于“右三角形”,然后将每条边离散为标志点数组,最后对每个跨度两侧的标志点范围内的像素着色,对于左三角形来说:边的左边是TRUE,边为FALSE;但是对于右三角形来说:边是TRUE,右边是FALSE;(void CTriangle::PhongShader(CDC * pDC, CP3 ViewPoint, CLighting * pLight, CMaterial * pMaterial, CTexture * pTexture) ),PhongShader是对像素点进行着色,但是对于GroundShader来说:就是对向量进行着色,将一个顶点作为面的颜色填充;
4.使用六张纹理图片:
首先,得读入位图,所以建立了一个Texture类,在这个类中,根据ID号来读入资源中的位图,并将位图保存到一维数组中,(void PrepareBitmap(UINT nIDResource);//准备位图),准备位图,将位图信息保存到数组中(NewBitmap.GetBitmapBits(nbytesize, (LPVOID)image)),然就将纹理绑定在立方体的顶点上,(CT2 ScreenTexture[4];//二维纹理坐标),然后进行纹理颜色替换属性(CT2 Texture = LinearInterp(x, SpanLeft[n].x, SpanRight[n].x, SpanLeft[n].t, SpanRight[n].t);),在PhongShader函数中,读取纹理位图的颜色数据(Texture.c = GetTexture(ROUND(Texture.u), ROUND(Texture.v), pTexture);),并将其设置为材质的漫反射率和环境反射率(pMaterial->SetDiffuse(Texture.c);pMaterial->SetAmbient(Texture.c);)。
三、实现方法
1.建立立方体类:
CCube.h:
#pragma once
#include"Projection.h"
#include"Facet.h"
#include"Triangle.h"
#include"Vector3.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 SetTexture(CTexture* pTexture);//设置纹理
void Draw(CDC* pDC);//绘制图形
private:
CP3 P[8];//点表
CFacet F[6];//面表
CProjection projection;//投影
CLighting* pLight;//光照
CMaterial* pMaterial;//材质
CTexture pTexture[6];
};
CCube.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::SetTexture(CTexture* pTexture)
{
this->pTexture[0] = pTexture[0];
this->pTexture[1] = pTexture[1];
this->pTexture[2] = pTexture[2];
this->pTexture[3] = pTexture[3];
this->pTexture[4] = pTexture[4];
this->pTexture[5] = pTexture[5];
}
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];//三维屏幕点
CT2 ScreenTexture[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.OrthogonalProjection3(P[F[nFacet].Index[nPoint]]);
pFill->SetPoint(ScreenPoint[0], ScreenPoint[2], ScreenPoint[3], FacetNormal, FacetNormal, FacetNormal, CT2(0, 0), CT2((pTexture[nFacet].bmp.bmWidth - 1), pTexture[nFacet].bmp.bmHeight - 1), CT2(0, pTexture[nFacet].bmp.bmHeight - 1));
pFill->PhongShader(pDC, ViewPoint, pLight, pMaterial, &pTexture[nFacet]);
pFill->SetPoint(ScreenPoint[0], ScreenPoint[1], ScreenPoint[2], FacetNormal, FacetNormal, FacetNormal, CT2(0, 0), CT2(pTexture[nFacet].bmp.bmWidth - 1, 0), CT2(pTexture[nFacet].bmp.bmWidth - 1, pTexture[nFacet].bmp.bmHeight - 1));
pFill->PhongShader(pDC, ViewPoint, pLight, pMaterial, &pTexture[nFacet]);
}
}
}
2.背面剔除消隐算法:
Vector3.h:
#pragma once
#include "P3.h"
class CVector3
{
public:
CVector3(void);
virtual ~CVector3(void);
CVector3(double x, double y, double z);//绝对向量
CVector3(const CP3 &p);
CVector3(const CP3 &p0, const CP3 &p1);//相对向量
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);//计算向量的叉积
public:
double x,y,z;
};
#include "pch.h"
#include "Vector3.h"
#include<math.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 &p)
{
x = p.x;
y = p.y;
z = p.z;
}
CVector3::CVector3(const CP3 &p0, const CP3 &p1)//相对向量
{
x = p1.x - p0.x;
y = p1.y - p0.y;
z = p1.z - p0.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;
}
判定条件:
if (DotProduct(ViewVector, FacetNormal)>=0)//背面剔除
3.PhongShader光照:
CTriangle.h:
#pragma once
#include "P3.h"//带颜色的浮点数二维点类
#include "Point3.h"//带颜色的整数三维点类
#include"Lighting.h"
#include"Material.h"
#include"Texture.h"
class CTriangle//三角形填充类
{
public:
CTriangle(void);
virtual ~CTriangle(void);
void SetPoint(CP3 P0, CP3 P1, CP3 P2, CVector3 N0, CVector3 N1, CVector3 N2, CT2 T0, CT2 T1, CT2 T2);
/*void GouraudShader(CDC* pDC, CP3 ViewPoint, CLighting* pLight, CMaterial* pMaterial, CTexture* pTexture);*/
void PhongShader(CDC* pDC, CP3 ViewPoint, CLighting* pLight, CMaterial* pMaterial, CTexture* pTexture);
private:
void EdgeFlag(CPoint2 PStart, CPoint2 PEnd, BOOL bFeature);//边标记
CVector3 LinearInterp(double t, double tStart, double tEnd, CVector3 vStart, CVector3 vEnd);//法向量线性插值
CT2 LinearInterp(double t, double tStart, double tEnd, CT2 texStart, CT2 texEnd);
void SortVertex(void);//三角形顶点排序
CRGB GetTexture(int u, int v, CTexture* pTexture);//读取纹理
private:
CP3 P0, P1, P2;//三角形的浮点数顶点
CPoint3 point0, point1, point2;//三角形的整数顶点坐标
CPoint2* SpanLeft; //跨度的起点数组标志
CPoint2* SpanRight;//跨度的终点数组标志
int nIndex;//扫描线索引
};
CTriangle.cpp:
#include "pch.h"
#include "Triangle.h"
#define ROUND(d) int(d + 0.5)
CTriangle::CTriangle(void)
{
}
CTriangle::~CTriangle(void)
{
}
void CTriangle::SetPoint(CP3 P0, CP3 P1, CP3 P2,CVector3 N0,CVector3 N1,CVector3 N2,CT2 T0,CT2 T1,CT2 T2)
{
this->P0 = P0, this->P1 = P1, this->P2 = P2;
point0.x = ROUND(P0.x);
point0.y = ROUND(P0.y);
point0.z = P0.z;
point0.c = P0.c;
point0.n = N0;
point0.t = T0;
point1.x = ROUND(P1.x);
point1.y = ROUND(P1.y);
point1.z = P1.z;
point1.c = P1.c;
point1.n = N1;
point1.t = T1;
point2.x = ROUND(P2.x);
point2.y = ROUND(P2.y);
point2.z = P2.z;
point2.c = P2.c;
point2.n = N2;
point2.t = T2;
}
void CTriangle::PhongShader(CDC * pDC, CP3 ViewPoint, CLighting * pLight, CMaterial * pMaterial, CTexture * pTexture)
{
double CurrentDepth = 0.0;//当前扫描线的深度
CVector3 Vector01(P0, P1), Vector02(P0, P2);
CVector3 fNormal = CrossProduct(Vector01, Vector02);
double A = fNormal.x, B = fNormal.y, C = fNormal.z;//平面方程Ax+By+Cz+D=0的系数
double D = -A * P0.x - B * P0.y - C * P0.z;//当前扫描线随着x增长的深度步长
if (fabs(C) < 1e-4)
C = 1.0;
double DepthStep = -A / C;//计算扫描线深度步长增量
SortVertex();
//定义三角形覆盖的扫描线条数
int nTotalLine = point1.y - point0.y + 1;
//定义span的起点与终点数组
SpanLeft = new CPoint2[nTotalLine];
SpanRight = new CPoint2[nTotalLine];
//判断三角形与P0P1边的位置关系,0-1-2为右手系
int nDeltz = (point1.x - point0.x) * (point2.y - point1.y) - (point1.y - point0.y) * (point2.x - point1.x);//点向量叉积的z坐标
if (nDeltz > 0)//三角形位于P0P1边的左侧
{
nIndex = 0;
EdgeFlag(point0, point2, TRUE);
EdgeFlag(point2, point1, TRUE);
nIndex = 0;
EdgeFlag(point0, point1, FALSE);
}
else//三角形位于P0P1边的右侧
{
nIndex = 0;
EdgeFlag(point0, point1, TRUE);
nIndex = 0;
EdgeFlag(point0, point2, FALSE);
EdgeFlag(point2, point1, FALSE);
}
for (int y = point0.y; y < point1.y; y++)//下闭上开
{
int n = y - point0.y;
BOOL bInFlag = FALSE;//跨度内外测试标志,初始值为假表示三角形外部
for (int x = SpanLeft[n].x; x < SpanRight[n].x; x++)//左闭右开
{
if (bInFlag == FALSE)
{
CurrentDepth = -(A * x + B * y + D) / C;//z=-(Ax+By+D)/C
bInFlag = TRUE;
x -= 1;
}
else
{
CVector3 ptNormal = LinearInterp(x, SpanLeft[n].x, SpanRight[n].x, SpanLeft[n].n, SpanRight[n].n);
ptNormal = ptNormal.Normalize();
/*-> 纹理颜色替换材质属性*/
CT2 Texture = LinearInterp(x, SpanLeft[n].x, SpanRight[n].x, SpanLeft[n].t, SpanRight[n].t);
Texture.c = GetTexture(ROUND(Texture.u), ROUND(Texture.v), pTexture);
pMaterial->SetDiffuse(Texture.c);
pMaterial->SetAmbient(Texture.c);
CRGB Intensity = pLight->Illuminate(ViewPoint, CP3(x, y, CurrentDepth), ptNormal, pMaterial);
pDC->SetPixelV(x, y, RGB(Intensity.red * 255, Intensity.green * 255, Intensity.blue * 255));
CurrentDepth += DepthStep;
}
}
}
if (SpanLeft)
{
delete[]SpanLeft;
SpanLeft = NULL;
}
if (SpanRight)
{
delete[]SpanRight;
SpanRight = NULL;
}
}
void CTriangle::EdgeFlag(CPoint2 PStart, CPoint2 PEnd, BOOL bFeature)
{
int dx = PEnd.x - PStart.x;
int dy = PEnd.y - PStart.y;
double m = double(dx) / dy;
double x = PStart.x;
for(int y = PStart.y; y < PEnd.y; y++)
{
CVector3 ptNormal = LinearInterp(y, PStart.y, PEnd.y, PStart.n, PEnd.n);
CT2 Texture = LinearInterp(y, PStart.y, PEnd.y, PStart.t, PEnd.t);
if (bFeature)
SpanLeft[nIndex++] = CPoint2(ROUND(x), y, ptNormal, Texture);
else
SpanRight[nIndex++] = CPoint2(ROUND(x), y, ptNormal, Texture);
x += m;
}
}
CVector3 CTriangle::LinearInterp(double t, double tStart, double tEnd, CVector3 vStart, CVector3 vEnd)
{
CVector3 vector;
vector = (tEnd - t) / (tEnd - tStart) * vStart + (t - tStart) / (tEnd - tStart) * vEnd;
return vector;
}
CT2 CTriangle::LinearInterp(double t, double tStart, double tEnd, CT2 texStart, CT2 texEnd)
{
CT2 texture;
texture = (t - tEnd) / (tStart - tEnd) * texStart + (t - tStart) / (tEnd - tStart) * texEnd;
return texture;
}
void CTriangle::SortVertex(void)
{
CPoint3 pt[3];
pt[0] = point0;
pt[1] = point1;
pt[2] = point2;
for (int i = 0; i < 2; i++)
{
int min = i;
for (int j = i + 1; j < 3; j++)
if (pt[j].y < pt[min].y)
min = j;
CPoint3 pTemp = pt[i];
pt[i] = pt[min];
pt[min] = pTemp;
}
point0 = pt[0];
point1 = pt[2];
point2 = pt[1];
}
CRGB CTriangle::GetTexture(int u, int v, CTexture * pTexture)
{
v = pTexture->bmp.bmHeight -1- v;
/*检测图片的边界,防止越界*/
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;
/*查找对应纹理空间的颜色值*/
int position = v * pTexture->bmp.bmWidthBytes + 4 * u;//颜色分量位置
return CRGB(pTexture->image[position + 2] / 255.0, pTexture->image[position + 1] / 255.0, pTexture->image[position] / 255.0);
}
PhongShader函数:
void CTriangle::PhongShader(CDC * pDC, CP3 ViewPoint, CLighting * pLight, CMaterial * pMaterial, CTexture * pTexture)
{
double CurrentDepth = 0.0;//当前扫描线的深度
CVector3 Vector01(P0, P1), Vector02(P0, P2);
CVector3 fNormal = CrossProduct(Vector01, Vector02);
double A = fNormal.x, B = fNormal.y, C = fNormal.z;//平面方程Ax+By+Cz+D=0的系数
double D = -A * P0.x - B * P0.y - C * P0.z;//当前扫描线随着x增长的深度步长
if (fabs(C) < 1e-4)
C = 1.0;
double DepthStep = -A / C;//计算扫描线深度步长增量
SortVertex();
//定义三角形覆盖的扫描线条数
int nTotalLine = point1.y - point0.y + 1;
//定义span的起点与终点数组
SpanLeft = new CPoint2[nTotalLine];
SpanRight = new CPoint2[nTotalLine];
//判断三角形与P0P1边的位置关系,0-1-2为右手系
int nDeltz = (point1.x - point0.x) * (point2.y - point1.y) - (point1.y - point0.y) * (point2.x - point1.x);//点向量叉积的z坐标
if (nDeltz > 0)//三角形位于P0P1边的左侧
{
nIndex = 0;
EdgeFlag(point0, point2, TRUE);
EdgeFlag(point2, point1, TRUE);
nIndex = 0;
EdgeFlag(point0, point1, FALSE);
}
else//三角形位于P0P1边的右侧
{
nIndex = 0;
EdgeFlag(point0, point1, TRUE);
nIndex = 0;
EdgeFlag(point0, point2, FALSE);
EdgeFlag(point2, point1, FALSE);
}
for (int y = point0.y; y < point1.y; y++)//下闭上开
{
int n = y - point0.y;
BOOL bInFlag = FALSE;//跨度内外测试标志,初始值为假表示三角形外部
for (int x = SpanLeft[n].x; x < SpanRight[n].x; x++)//左闭右开
{
if (bInFlag == FALSE)
{
CurrentDepth = -(A * x + B * y + D) / C;//z=-(Ax+By+D)/C
bInFlag = TRUE;
x -= 1;
}
else
{
CVector3 ptNormal = LinearInterp(x, SpanLeft[n].x, SpanRight[n].x, SpanLeft[n].n, SpanRight[n].n);
ptNormal = ptNormal.Normalize();
/*-> 纹理颜色替换材质属性*/
CT2 Texture = LinearInterp(x, SpanLeft[n].x, SpanRight[n].x, SpanLeft[n].t, SpanRight[n].t);
Texture.c = GetTexture(ROUND(Texture.u), ROUND(Texture.v), pTexture);
pMaterial->SetDiffuse(Texture.c);
pMaterial->SetAmbient(Texture.c);
CRGB Intensity = pLight->Illuminate(ViewPoint, CP3(x, y, CurrentDepth), ptNormal, pMaterial);
pDC->SetPixelV(x, y, RGB(Intensity.red * 255, Intensity.green * 255, Intensity.blue * 255));
CurrentDepth += DepthStep;
}
}
}
if (SpanLeft)
{
delete[]SpanLeft;
SpanLeft = NULL;
}
if (SpanRight)
{
delete[]SpanRight;
SpanRight = NULL;
}
}
4.使用六张纹理照片:
CTexture.h:
#pragma once
#include"resource.h"
class CTexture
{
public:
CTexture(void);
virtual~CTexture(void);
void PrepareBitmap(UINT nIDResource);//准备位图
void DeleteObject(void);//释放位图
public:
BYTE* image;
BITMAP bmp;//BITMAP结构体变量
};
CTexture.cpp:
#include "pch.h"
#include "Texture.h"
CTexture::CTexture(void)
{
image = NULL;
}
CTexture::~CTexture(void)
{
}
void CTexture::PrepareBitmap(UINT nIDResource)//准备位图
{
CBitmap NewBitmap;
NewBitmap.LoadBitmap(nIDResource);
NewBitmap.GetBitmap(&bmp);//将CBitmap的信息保存到Bitmap结构体中
int nbytesize = bmp.bmWidthBytes * bmp.bmHeight;
image = new BYTE[nbytesize];
NewBitmap.GetBitmapBits(nbytesize, (LPVOID)image);//将我们位图的信息复制到image数组中
}
void CTexture::DeleteObject(void)//释放位图
{
if(NULL != image)
delete []image;
}
在TestView.h中定义:
CCube cube;
double Alpha, Beta;
CTransform3 transform;
BOOL bPlay;//动画按钮
int nLightSourceNumber;//光源数量
CLighting* pLight;//光照环境
CMaterial* pMaterial;//物体材质
CTexture texture[6];//纹理
在TestView.cpp中定义:
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);//设置场景
texture[0].PrepareBitmap(IDB_BITMAP1);
texture[1].PrepareBitmap(IDB_BITMAP2);
texture[2].PrepareBitmap(IDB_BITMAP3);
texture[3].PrepareBitmap(IDB_BITMAP4);
texture[4].PrepareBitmap(IDB_BITMAP5);
texture[5].PrepareBitmap(IDB_BITMAP6);
cube.SetTexture(texture);
初始化光照:
void CTestView::InitializeLightingScene(void)//初始化光照环境
{
//设置光源属性
nLightSourceNumber = 1;//光源个数
pLight = new CLighting(nLightSourceNumber);//一维光源动态数组
pLight->LightSource[0].SetPosition(1000, 1000, 1000);//设置光源位置坐标
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.847, 0.10, 0.075));//环境反射率
pMaterial->SetDiffuse(CRGB(0.852, 0.006, 0.026));//漫反射率
pMaterial->SetSpecular(CRGB(0.3, 0.3, 0.3));//镜面反射率
pMaterial->SetEmission(CRGB(0.0, 0.0, 0.0));//自身辐射的颜色
pMaterial->SetExponent(50);//高光指数
}
绘制:
void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
DoubleBuffer(pDC);
}
DoubelBuffer函数:
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::OnGraphAnimation()
{
// TODO: Add your command handler code here
bPlay = !bPlay;
if (bPlay)//设置定时器
SetTimer(1, 150, NULL);
else
KillTimer(1);
}
void CTestView::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
Alpha = 5, Beta = 5;
transform.RotateX(Alpha);
transform.RotateY(Beta);
Invalidate(FALSE);
CView::OnTimer(nIDEvent);
}
void CTestView::OnUpdateGraphAnimation(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
if (bPlay)
pCmdUI->SetCheck(TRUE);
else
pCmdUI->SetCheck(FALSE);
}
方向键动画:
void CTestView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
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);
}
四、实验结果分析
实验结果
实验总结
在这次实验中,对于纹理的贴图的个数要搞清楚,否则就会出现异常,关键是还找不到,就是提示异常,所以要注意这块的问题;在进行PhongShader函数的时候,要注意纹理那块的结合,否则又会出现异常;背面剔除算法,如果进行不当,就会发现少了1那个贴图;在背面剔除算法中,要注意最多只能看见三个面,因为这里都是单位化,所以这里要注意角度的正负就说明了哪些面可以看见,哪些面是不可以看见哒,本次实验,由于立方体是一个封闭结构,可以使用背面剔除算法,如果遇见非封闭的结构就不可以使用背面剔除算法。
参考文献
计算机图形学书;
在这个实验中,周围同学的帮助是很大哒。