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在下面
图形交叉投影