Z-buffer消隐算法与活性边表

Z-buffer消隐算法与活性边表

初始时我们设置 Z b u f f e r Zbuffer Zbuffer 二维数组,并且每个深度初始值设为 − D B L _ M A X -DBL\_MAX DBL_MAX(double类型变量最小负数),借用活性边表结构存储多边形的结构,利用扫描线算出交点坐标。
假设我们得到 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),那么我们可以通过 Z b u f f e r [ x 1 ] [ y 1 ] Zbuffer[x_1][y_1] Zbuffer[x1][y1] ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 处的深度值进行比较,重新填写深度值并将该点的颜色重新写入。
由于我们需要花两个多边形,所以我们要扫描两次,第一次画第一个图形,并且深度值更新为第一个图像的深度值,第二个多边形在填充的时候则会考虑深度值的比较来决定是否重新上色。
深度值的计算:考虑深度值增量: Δ z = − a c Δz=-\frac{a}{c} Δz=ca,我们能够通过计算得到某一条扫描线最左侧交点的深度值,通过深度值增量计算之后的像素点的深度值,即假设 Z ( x 1 , y 1 ) = z 0 Z(x_1,y_1)=z_0 Z(x1,y1)=z0,那么 Z ( x 1 + 1 , y 1 ) = z 0 + Δ z Z(x_1+1,y_1)=z_0+\Delta z Z(x1+1,y1)=z0+Δz

代码

#include <GL/glut.h>
#include <windows.h>
#include <iostream>

const int POINTNUM = 7;
//窗口属性结构体
struct Window {
	int width;
	int height;
}WindowSize = { 2000, 1000 };
//链表的实现
typedef struct ET {
	float x;
	float dx, ymax;
	//int id;
	ET* next;
}AET, NET;
//点结构体
struct point {
	float x;
	float y;
}
polypoint[2][POINTNUM] =
{ {500, 100, 1000, 550, 550, 400, 250, 300, 100, 700, 50, 100, 120, 20},
  {1200, 800, 570, 400, 300, 700, 220, 150, 600, 200, 1000, 400, 1100, 100} };//两个图形顶点坐标的位置

double** Zbuffer; // 深度缓冲区

// 平面系数
struct plane {
	double a;
	double b;
	double c;
	double d;
}
//polyplane[2] = { {8.0, 4.0, 5.0, 5.0}, {2.0, 8.0, 4.5, 1.3} }; // 图形2在上面
//polyplane[2] = { {8.0, 16.1, -5.3, 5.0}, {2.0, 8.0, 4.5, 1.3} }; // 图形2在下面
polyplane[2] = { {8.0, -6.1, 5.3, -5.0}, {-2.0, 8.0, 4.5, 1.3} }; // 图形交叉投影

// RGB颜色结构体
struct RGB3f {
	float red;
	float green;
	float blue;
}
rgb[2] = { {0.0, 0.0, 0.0},{1.0, 0.0, 0.0} };

double depth(plane& pt, double x, double y) {
	double depth_v;
	depth_v = -(pt.a * x + pt.b * y + pt.d) / pt.c;
	return depth_v;
}

void PolyScan() {
	Zbuffer = new double* [WindowSize.height];
	for (int i = 0; i < WindowSize.height; i++) {
		Zbuffer[i] = new double[WindowSize.width];
	}
	for (int i = 0; i < WindowSize.height; i++) {
		for (int j = 0; j < WindowSize.width; j++)
			Zbuffer[i][j] = -DBL_MAX;
	}
	glClear(GL_COLOR_BUFFER_BIT);
	glBegin(GL_POINTS);
	//glColor3f(rgb[k].red, rgb[k].green, rgb[k].blue);
	//glColor3f(rgb[1].red, rgb[1].green, rgb[1].blue);
	for (int k = 0; k < 2; k++) {
		int MaxY = 0;
		int i;
		glColor3f(rgb[k].red, rgb[k].green, rgb[k].blue);
		for (i = 0; i < POINTNUM; i++) {
			if (polypoint[k][i].y > MaxY)
				MaxY = polypoint[k][i].y;
		}
		//}
		//选出最大的顶点所对应的y值
		AET* pAET = new AET;
		pAET->next = NULL;
		NET* pNET[1024];

		for (i = 0; i <= MaxY; i++) {
			pNET[i] = new NET;
			pNET[i]->next = NULL;
		}//初始化扫描边的活性边表
		//一个点跟前面的点形成一条线段,同时跟后面一个点形成线段
		for (i = 0; i < MaxY; i++) {
			for (int j = 0; j < POINTNUM; j++) {
				if (polypoint[k][j].y == i) {
					if (polypoint[k][(j - 1 + POINTNUM) % POINTNUM].y > polypoint[k][j].y) {
						NET* p = new NET;
						p->x = polypoint[k][j].x;
						p->ymax = polypoint[k][(j - 1 + POINTNUM) % POINTNUM].y;
						p->dx = (polypoint[k][(j - 1 + POINTNUM) % POINTNUM].x - polypoint[k][j].x) / (polypoint[k][(j - 1 + POINTNUM) % POINTNUM].y - polypoint[k][j].y);
						p->next = pNET[i]->next;
						//p->id = k;
						pNET[i]->next = p;
					}

					if (polypoint[k][(j + 1 + POINTNUM) % POINTNUM].y > polypoint[k][j].y) {
						NET* p = new NET;
						p->x = polypoint[k][j].x;
						p->ymax = polypoint[k][(j + 1 + POINTNUM) % POINTNUM].y;
						p->dx = (polypoint[k][(j + 1 + POINTNUM) % POINTNUM].x - polypoint[k][j].x) / (polypoint[k][(j + 1 + POINTNUM) % POINTNUM].y - polypoint[k][j].y);
						p->next = pNET[i]->next;
						//p->id = k;
						pNET[i]->next = p;
					}
				}
			}
		}
		//}
		//把新边表net[i]中的边节点插入AET表,保证x递增
		for (i = 0; i <= MaxY; i++) {
			NET* p = pAET->next;
			while (p) {
				p->x = p->x + p->dx;
				p = p->next;
			}
			AET* tq = pAET;
			p = pAET->next;
			tq->next = NULL;
			while (p) {
				while (tq->next && p->x >= tq->next->x)//重新排序
					tq = tq->next;
				NET* s = p->next;
				p->next = tq->next;
				tq->next = p;
				p = s;
				tq = pAET;
			}
			//遍历AET表
			AET* q = pAET;
			p = q->next;
			while (p) {
				if (p->ymax == i) {
					q->next = p->next;
					delete p;
					p = q->next;
				}
				else {
					q = q->next;
					p = q->next;
				}
			}
			p = pNET[i]->next;
			q = pAET;
			while (p) {
				while (q->next && p->x >= q->next->x)
					q = q->next;
				NET* s = p->next;
				p->next = q->next;
				q->next = p;
				p = s;
				q = pAET;
			}
			p = pAET->next;
			double x_z;
			while (p && p->next) {
				// 初始化x最小值处的深度值
				x_z = depth(polyplane[k], (double)(p->x), static_cast<double>(i));

				for (float j = p->x; j <= p->next->x; j++) {
					if (j > p->x) // 按照深度值增量计算后续深度值
						x_z = x_z - polyplane[k].a / polyplane[k].c;
					if (x_z > Zbuffer[i][static_cast<int>(j)]) { // 如果图形深度值比当前像素点深度值大,那么进行绘制
						glVertex2i(static_cast<int>(j), i);//改写像素的颜色值
						Zbuffer[i][static_cast<int>(j)] = x_z;
					}
				}
				p = p->next->next;
			}
		}
	}
	glEnd();
	glFlush();
}


void changeSize(GLsizei w, GLsizei h)
{
	if (h == 0)
		h = 1;
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluOrtho2D(0.0,
		static_cast<double>(WindowSize.width) * w * 1.0f / static_cast<double>(WindowSize.width),
		0.0,
		static_cast<double>(WindowSize.height) * h * 1.0f / static_cast<double>(WindowSize.height));

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void Display() {

	PolyScan();

}


int main(int argc, char* argv) {
	glutInit(&argc, &argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(50, 100);
	glutInitWindowSize(WindowSize.width, WindowSize.height);
	glutCreateWindow("Z-buffer");
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glMatrixMode(GL_PROJECTION);
	gluOrtho2D(0.0, static_cast<double>(WindowSize.width), 0.0, static_cast<double>(WindowSize.height));
	glutReshapeFunc(changeSize);    //注册窗口大小改变时回调函数
	glutDisplayFunc(Display);
	glutMainLoop();
}

运行结果

图形2在上面

请添加图片描述

图形2在下面请添加图片描述

图形交叉投影
请添加图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值