实现渐变彩色消隐旋转立方体

实现渐变彩色消隐旋转立方体

效果图:
在这里插入图片描述

其总体设计流程图为如下图所示:
在这里插入图片描述通过在Microsoft Visual Studio 2010开发环境下MFC类库搭建所需要的框架,创建应用程序框架,建立一个工程名称为Z_BufferEli的MFC单文档应用程序框架。运用框架制作该立体彩色渐变图形,建立一个基本MFC单文档应用基础程序,然后再在改程序的基础之上,添加了MyFace.h文件,该文件主要定义三个用于处理点,线,面的类,分别为:class DPoint、class DLine、class Dface。接着创建一个Matrix.h文件,主要用于矩阵处理以及计算。接着添加类CZ_BufferElilView的公有成员变量和成员函数。然后应用MFC的向导,添加视图类CZ_BufferElilView的消息映射WM_KEYDOWN。接着修改Z_BufferElilView.cpp中的代码,添加构造函数初始化各个变量,添加判断点是否在多边形内部的函数,添加对单个面z缓冲处理的函数,添加OnDraw绘制图形的代码,添加按键旋转函数OnKeyDown,到这里完成了对旋转立方体的设计。
接着在FaceBuffer函数中修改代码,添加3个字节形的数组,分别存储9种颜色的RGB值,分别有白、红、橙、黄、绿、蓝、靛、紫、黑。然后将函数改为按步长变化RGB值,使得输出的颜色实现多种颜色的渐变,丰富了图形的颜色,使得图形更加绚丽。

1. 打开VS2010->“新建项目”->“MFC应用程序”->名称:“Z_BufferEli” ->“确定”。
“下一步”->应用程序类型:单个文档;项目类型:MFC标准->“完成”。

在这里插入图片描述
在这里插入图片描述
2.右键头文件—>点击“添加”—>“新建项”,之后输入文件名MyFace,定义MyFace.h文件,该文件主要定义三个用于处理点,线,面的类。
在这里插入图片描述
在这里插入图片描述
输入的代码如下:

点类
class DPoint
{
public:
	DPoint(){x=0;y=0;z=0;}            //默认构造函数
	DPoint(int a,int b,int c):x(a),y(b),z(c){}   //构造函数
	DPoint(const DPoint&p):x(p.x),y(p.y),z(p.z){}  //复制构造函数
	DPoint&operator=(const DPoint&rhs)           //赋值操作
	{
		x=rhs.x;y=rhs.y;z=rhs.z;
		return *this;
	}
	friend inline int operator==(const DPoint&p1,const DPoint&p2)  //判断点是否相等,是返回1,否则返回0
	{
		if (p1.x==p2.x&&p1.y==p2.y&&p1.z==p2.z)
		{
			return 1;
		}
		else
			return 0;
	}
	friend inline int operator!=(const DPoint&p1,const DPoint&p2)  //判断点是否不相等,是返回0,否则返回1
	{
		if ((p1.x!=p2.x)||(p1.y!=p2.y)||(p1.z!=p2.z)) 
		{
			return 1;
		}
		else
			return 0;
	}
	void setx(int a)
	{
		x=a;
	}
	void sety(int b)
	{
		y=b;
	}
	void setz(int c)
	{
		z=c;
	}
	int getx()
	{
		return x;
	}
	int gety()
	{
		return y;
	}
	int getz()
	{
		return z;
	}
protected:
public:
	int x;
	int y;
	int z;
};


/线类
class DLine
{
public:
	DLine(int a,int b,int c,int d,int e,int f):p1(a,b,c),p2(d,e,f){}     //构造函数
	DLine():p1(0,0,0),p2(0,0,0){}                            //默认构造函数
	DLine(DPoint x,DPoint y):p1(x),p2(y){}
	DPoint getp1()
	{
		return p1;
	}
	DPoint getp2()
	{
		return p2;
	}
	DLine(const DLine&l)                                 //复制构造函数
	{
		p1=l.p1;
		p2=l.p2;
	}
	DLine&operator=(const DLine&rhs)                     //赋值操作
	{
		p1=rhs.p1;p2=rhs.p2;
		return *this;
	}

	friend int intersect(const DLine&l1,const DLine&l2)         //判断线段是否有公共点,有返回1,否则返回0
	{
		if ((l1.p1==l2.p1)||(l1.p1==l2.p2)||(l1.p2==l2.p1)||(l1.p2==l2.p2))
		{
			return 1;
		}
		else 
			return 0;
	}
protected:
private:
	DPoint p1;
	DPoint p2;
};

面类
class DFace
{
public:
	DFace():p1(0,0,0),p2(0,0,0),p3(0,0,0),p4(0,0,0),L1(0,0,0,0,0,0),L2(0,0,0,0,0,0),L3(0,0,0,0,0,0),L4(0,0,0,0,0,0),a(0),b(0),c(0),d(0)
	{
//		a=b=c=d=0;
	}  //默认构造函数
	DFace(DPoint x,DPoint y,DPoint z,DPoint w):p1(x),p2(y),p3(z),p4(w),L1(x,y),L2(y,z),L3(z,w),L4(w,x)   //构造函数
	{
		int x1=x.getx(),y1=x.gety(),z1=x.getz();
		int x2=y.getx(),y2=y.gety(),z2=y.getz();
		int x3=z.getx(),y3=z.gety(),z3=z.getz();
		int x4=w.getx(),y4=w.gety(),z4=w.getz();
		a=(int)((y1-y2)*(z1+z2)+(y2-y3)*(z2+z3)+(y3-y4)*(z3+z4)+(y4-y1)*(z4+z1));
		b=(int)((z1-z2)*(x1+x2)+(z2-z3)*(x2+x3)+(z3-z4)*(x3+x4)+(z4-z1)*(x4+x1));
		c=(int)((x1-x2)*(y1+y2)+(x2-x3)*(y2+y3)+(x3-x4)*(y3+y4)+(x4-x1)*(y4+y1));
		d=(int)(-(a*x1+b*y1+c*z1));
	}
	int geta()
	{
		return a;
	}
	int getb()
	{
		return b;
	}
	int getcc()
	{
		return c;
	}
	int getd()
	{
		return d;
	}

private:
	DPoint p1,p2,p3,p4;
	DLine L1,L2,L3,L4;
	int a;
	int b;
	int c;
	int d;
};

3.用2的方法创建Matrix.h文件,用于矩阵处理以及计算。

#pragma once

class Matrix
{
public:
	int ROW;                       //行数
	int COL;                       //列数
	double **p; 
public:
	Matrix(int m=0,int n=0,double *A=NULL,int k=0)//;	       //矩阵的构造函数
	{
		ROW=m;                         //指定行数
		COL=n;                         //指定列数                             
		p=new double*[ROW];               //生成元素为指针,长度为ROW的数组
		for(int i=0;i<ROW;++i)
		{
			p[i]=new double[COL];         //p[i]是长度为COL的数组的指针
			for (int j=0;j<COL;++j)
			{
				p[i][j]=A[i*n+j];          //用一维数组构造二维数
			}	
		}
	}
	~Matrix()//;                     //矩阵的析构函数
	{
		for(int i=0;i<ROW;++i)
		{
			delete []p[i];             //释放数组 
		}
	}

	Matrix&operator=(Matrix&s)//;     //赋值运算符重载
	{
		ROW=s.ROW;
		COL=s.COL;
		p=new double*[ROW];               //生成元素为指针,长度为ROW的数组
		for(int i=0;i<ROW;++i)
		{
			p[i]=new double[COL];         //p[i]是长度为COL的数组的指针
			for (int j=0;j<COL;++j)
			{
				p[i][j]=s.p[i][j];          //用一维数组构造二维数
			}	
		}
		return *this;
	}
	Matrix&operator*(Matrix&t)//;       //用友元声明乘法运算符重载
	{
		int m=ROW,n=COL,k=t.COL;
		Matrix result;
		result.ROW=m;result.COL=k;
		result.p=new double*[m];
		for(int i=0;i<m;++i)
		{	
			result.p[i]=new double[k]; 
			for(int j=0;j<k;++j)
			{
				result.p[i][j]=0;
				for(int l=0;l<n;++l)
				result.p[i][j]+=p[i][l]*t.p[l][j];
			}
		}
		ROW=result.ROW;COL=result.COL;
		for (int i=0;i<ROW;++i)
		{
			for (int j=0;j<COL;++j)
			{
				p[i][j]=result.p[i][j];
			}
		}
		return *this;	
	}
};

4.在Z_BufferEliView.h中添加类CZ_BufferElilView的公有成员变量和成员函数的定义。

public:
    int n;
	int num[1000];
	COLORREF color[400][400];//帧缓存
	double z_distance[400][400];//z缓存
	float angle;//旋转角度
	DPoint P1,P2,P3,P4,P5,P6,P7,P8;//立方体的八个顶点
	int IsInSide(CPoint Px,CPoint P1,CPoint P2,CPoint P3,CPoint P4);//判断一个点是否在多边形内
	void FaceBuffer(DPoint P1,DPoint P2,DPoint P3,DPoint P4,int start,int end,CDC*pDC);

添加头文件:

#include "MyFace.h"

5. 打开应用MFC向导添加视图类CZ_BufferElilView的消息映射WM_KEYDOWN

在这里插入图片描述在这里插入图片描述
6. 在Z_BufferElilView.cpp中添加程序结构代码。
1) 添加头文件

#include "math.h"
#include "Matrix.h"

2) CZ_BufferEliView的构造函数中对变量进行初始化。

CZ_BufferEliView::CZ_BufferEliView():P1(-100,100,100),P2(100,100,100),P3(100,100,-100),P4(-100,100,-100),P5(-100,-100,100),P6(100,-100,100),P7(100,-100,-100),P8(100,-100,-100)
{
	// 初始化帧缓存和Z缓存
	angle=float(3.1415926/3);
	for (int i=0;i<400;i++)
	{
		for (int j=0;j<400;++j)
		{
			color[i][j]=RGB(255,255,255);//
			z_distance[i][j]=-1000;
		}
	}
	n=0;
}

3)添加判断一个点是否在多边形内部的函数。

int CZ_BufferEliView::IsInSide(CPoint Px, CPoint P1, CPoint P2, CPoint P3, CPoint P4)
{
	double a1,b1,a2,b2,a3,b3,a4,b4;
	int m=0;	int x=(P1.x+P3.x)/2,y=(P1.y+P3.y)/2;//P1P3中点坐标
	if ((P1.x!=P2.x)&&(P1.x!=P4.x)) 
	{	
		a1=((double)(P2.y-P1.y))/(P2.x-P1.x);//P1P2斜率
		b1=P1.y-a1*P1.x;//P1P2截距
		a2=((double)(P3.y-P2.y))/(P3.x-P2.x);
		b2=P2.y-a2*P2.x;
		a3=a1;
		b3=P3.y-a1*P3.x;
		a4=a2;
		b4=P1.y-a4*P1.x;
		if (((a1*Px.x+b1-Px.y)*(a1*x+b1-y)>0)&&((a3*Px.x+b3-Px.y)*(a3*x+b3-y)>0)&&((a2*Px.x+b2-Px.y)*(a2*x+b2-y)>0)&&((a4*Px.x+b4-Px.y)*(a4*x+b4-y)>0))
		{
			m++;
		}
	}
	else if (P1.x==P2.x)//水平边的处理
	{
		a2=((double)(P3.y-P2.y))/(P3.x-P2.x);
		b2=P2.y-a2*P2.x;
		a4=a2;
		b4=P1.y-a4*P1.x;
		if (P4.x>P1.x)
		{	
			if (((a2*x+b2-y)*(a2*Px.x+b2-Px.y)>0)&&((a4*x+b4-y)*(a4*Px.x+b4-Px.y)>0)&&(P1.x<Px.x)&&(Px.x<P4.x))
			{
				m++;
			}
		}
		else
		{
			if (((a2*x+b2-y)*(a2*Px.x+b2-Px.y)>0)&&((a4*x+b4-y)*(a4*Px.x+b4-Px.y)>0)&&(P4.x<Px.x)&&(Px.x<P1.x))
			{
				m++;
			}
		}
	
	}
	else if (P1.x==P4.x)//水平边的处理
	{
		a1=((double)(P2.y-P1.y))/(P2.x-P1.x);
		b1=P1.y-a1*P1.x;
		a3=a1;
		b3=P3.y-a1*P3.x;
		if (P2.x>P1.x)
		{
			if (((a1*x+b1-y)*(a1*Px.x+b1-Px.y)>0)&&((a3*x+b3-y)*(a3*Px.x+b3-Px.y)>0)&&(P1.x<Px.x)&&(Px.x<P2.x))
			{
				m++;
			}
		}
		else
		{
			if (((a1*x+b1-y)*(a1*Px.x+b1-Px.y)>0)&&((a3*x+b3-y)*(a3*Px.x+b3-Px.y)>0)&&(P2.x<Px.x)&&(Px.x<P1.x))
			{
				m++;
			}
		}
	}
	return m;
}

4) 对单个面的Z缓冲处理函数和对颜色的渐变变化

void CZ_BufferEliView::FaceBuffer(DPoint P1, DPoint P2, DPoint P3, DPoint P4, int start,int end,CDC *pDC)
{
	CPoint p[4];int xleft,xright,ytop,ybottom;
	DFace f(P1,P2,P3,P4);
	int a=f.geta(),b=f.getb(),c=f.getcc(),d=f.getd();
	p[0].x=P1.getx();p[0].y=P1.gety();
	p[1].x=P2.getx();p[1].y=P2.gety();
	p[2].x=P3.getx();p[2].y=P3.gety();
	p[3].x=P4.getx();p[3].y=P4.gety();
	//确定平面的X,Y范围
	xleft=xright=p[0].x;ytop=ybottom=p[0].y;
	for (int i=0;i<4;++i)
	{
		if (p[i].x<xleft)
		{
			xleft=p[i].x;
		}
		if (p[i].x>xright)
		{
			xright=p[i].x;
		}
		if (p[i].y<ybottom) 
		{
			ybottom=p[i].y;
		}
		if (p[i].y>ytop)
		{
			ytop=p[i].y;
		}
	}

	BYTE r[9]={255,255,250,255,0,68,75,141,0};
	BYTE g[9]={255,33,140,255,229,206,0,75,0};
	BYTE blue[9]={255,33,53,0,0,246,130,187,0,};
	double width=(xright-xleft)/4;

	BYTE r9;
	BYTE g9;
	BYTE b9;

	//对XY范围内的点逐个判断是否在多边形内
	for (int y=ybottom;y<=ytop;++y)
	{
		int x=xleft;
		for(int i=start;i<=end;i++)
		{
			double re,gr,bl;
			re=(double)(r[i+1]-r[i])/width;
			gr=(double)(g[i+1]-g[i])/width;
			bl=(double)(blue[i+1]-blue[i])/width;
			for(int k=0;k<=width;k++,x++)
			{
				r9=r[i]+k*re;
				g9=g[i]+k*gr;
				b9=blue[i]+k*bl;
				CPoint mid(x,y);
				if (IsInSide(mid,p[0],p[1],p[2],p[3])) 
				{
					if (c!=0)
					{	
						double z=((double)(-d-a*x-b*y))/c;
						//z缓冲的过程
						if (z>=z_distance[x+200][y+200])
						{
							color[x+200][y+200]=RGB(r9,g9,b9);
							z_distance[x+200][y+200]=z;
						}
					}
				}
			
			}
		}
	}
}

5) OnDraw()函数中添加绘制图形的代码。

void CZ_BufferEliView::OnDraw(CDC* pDC)
{
	CZ_BufferEliDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	pDC->TextOut(0,0,_T("按\"X\"、\"Y\"、\"Z\"键从各方位观看图形"));
	// TODO: Add your message handler code here
	CRect rect;
	GetClientRect(&rect);
	pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);
	//每次重绘前重新初始化帧缓存和Z缓存
	for (int i=0;i<400;++i)
	{
		for (int j=0;j<400;++j)
		{
			color[i][j]=RGB(255,255,255);
			z_distance[i][j]=-1000;
		}
	}
	FaceBuffer(P1,P2,P3,P4,0,3,pDC);
	FaceBuffer(P5,P6,P7,P8,1,4,pDC);
	FaceBuffer(P3,P4,P8,P7,2,5,pDC);
	FaceBuffer(P1,P2,P6,P5,3,6,pDC);
	FaceBuffer(P2,P3,P7,P6,4,7,pDC);
	FaceBuffer(P1,P4,P8,P5,5,8,pDC);
	for (int y=-200;y<200;++y)
	{
		for (int x=-200;x<200;++x)
		{
			pDC->SetPixel(x,y,color[x+200][y+200]);
		}
	}
	P1=DPoint(-100,100,100);P2=DPoint(100,100,100);
	P3=DPoint(100,100,-100);P4=DPoint(-100,100,-100);
	P5=DPoint(-100,-100,100);P6=DPoint(100,-100,100);
	P7=DPoint(100,-100,-100);P8=DPoint(-100,-100,-100);
}

6) 添加OnKeyDown函数,通过按X,Y,Z键可以旋转立方体,从不同角度来观察。

void CZ_BufferEliView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	double A[16]={1,0,0,0,0,cos(angle),-sin(angle),0,0,sin(angle),cos(angle),0,0,0,0,1};
	Matrix RX(4,4,A,16);

	double B[16]={cos(angle),0,sin(angle),0,0,1,0,0,-sin(angle),0,cos(angle),0,0,0,0,1};
	Matrix RY(4,4,B,16);

	double C[16]={cos(angle),-sin(angle),0,0,sin(angle),cos(angle),0,0,0,0,1,0,0,0,0,1};
	Matrix RZ(4,4,C,16);

	double D[16]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
	Matrix M(4,4,D,16);
switch(nChar){			
	case 88://按下X,Y,Z键 图形绕三个轴旋转
		num[n]=1;
		break;
	case 89:
		num[n]=2;
		break;
	case 90:
		num[n]=3;
		break;

	default:
		break;
	}
//保证多次旋转仍能正确显示
for (int j=0;j<=n;++j)
	{
		if (num[j]==1)
		{
			M=RX*M;
		}
		if (num[j]==2)
		{
			M=RY*M;
		}
		if (num[j]==3)
		{
			M=RZ*M;
		}		
	}
	n++;
	DPoint p[8];
	p[0]=P1;p[1]=P2;p[2]=P3;p[3]=P4;
	p[4]=P5;p[5]=P6;p[6]=P7;p[7]=P8;
	for (int i=0;i<8;++i)
	{
		double x,y,z;
		x=M.p[0][0]*p[i].x+M.p[0][1]*p[i].y+M.p[0][2]*p[i].z+M.p[0][3];
		y=M.p[1][0]*p[i].x+M.p[1][1]*p[i].y+M.p[1][2]*p[i].z+M.p[1][3];
		z=M.p[2][0]*p[i].x+M.p[2][1]*p[i].y+M.p[2][2]*p[i].z+M.p[2][3];
		p[i].x=(int)(x);p[i].y=(int)(y);p[i].z=(int)(z);
	}
	P1=p[0];P2=p[1];P3=p[2];P4=p[3];
	P5=p[4];P6=p[5];P7=p[6];P8=p[7];
	Invalidate();		
	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

最终结果:

在这里插入图片描述

改变默认的参数,也可以改变渐变的颜色数量
在这里插入图片描述
有问题欢迎随时指导。

[1] 潘云鹤,童若锋,唐敏. 计算机图形学. 高等教育出版社,2011.
[2] 博客园博主JustDoIT 原文链接:http://www.cnblogs.com/TenosDoIt/p/4024413.html
[3] CSDN博主Jurb原文链接:https://blog.csdn.net/Jurbo/article/details/75007260
[4] CSDN博主mycaibo原文链接:https://blog.csdn.net/mycaibo/article/details/6012063

  • 2
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
three.js是一个用于创建3D图形的JavaScript库。它提供了丰富的功能和工具,可以轻松地在Web浏览器中创建交互式的3D场景和动画效果。 渐变圆边立方体是指一个立方体,其边缘呈现出渐变的圆形效果。在three.js中,可以通过以下步骤来创建渐变圆边立方体: 1. 创建一个立方体的几何体(BoxGeometry)并设置其大小和位置。 2. 创建一个渐变材质(MeshBasicMaterial)并设置其颜色属性为渐变色。 3. 将几何体和材质结合起来创建一个网格(Mesh)对象。 4. 将网格对象添加到场景中。 5. 创建一个渲染器(WebGLRenderer)并将其连接到HTML文档中的一个元素上。 6. 在每一帧中更新场景和相机,并使用渲染器进行渲染。 下面是一个示例代码,展示了如何使用three.js创建一个渐变圆边立方体: ```javascript // 创建场景 var scene = new THREE.Scene(); // 创建相机 var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 5; // 创建渲染器 var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 创建立方体几何体 var geometry = new THREE.BoxGeometry(1, 1, 1); // 创建渐变材质 var material = new THREE.MeshBasicMaterial({ color: new THREE.Color(0xff0000), // 起始颜色 vertexColors: THREE.VertexColors // 使用顶点颜色 }); // 设置顶点颜色 geometry.faces.forEach(function(face) { var color = new THREE.Color(Math.random(), Math.random(), Math.random()); face.vertexColors.push(color, color, color); }); // 创建立方体网格 var cube = new THREE.Mesh(geometry, material); // 将立方体添加到场景中 scene.add(cube); // 渲染函数 function render() { requestAnimationFrame(render); // 旋转立方体 cube.rotation.x += 0.01; cube.rotation.y += 0.01; // 渲染场景和相机 renderer.render(scene, camera); } // 开始渲染 render(); ``` 这是一个基本的示例,你可以根据自己的需求进行修改和扩展。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值