图形学作业要到deadline了,赶紧写一个
这个算法比之前的算法的工作量都要大,但是只要思路清晰,也不是很难。
1.创建各种需要的数据结构类

//点的类
class Point
{public:float x;float y;float z;Point();~Point();
};
//y表
class YTable
{public:int m_IndexOfPolygon;float m_Ymax;YTable * next;YTable();YTable(int IndexOfPolygon,float Ymax);~YTable();
};
//边表
class ET
{public:float m_Ymax;float m_Yminx;float m_Yminz;float m_DeltaX;ET *next;ET();~ET();
};
//活性边对表
class AET
{public:float m_XL;float m_DeltaXL;float m_Lmax;float m_XR;float m_DeltaXR;float m_Rmax;float m_ZL;int m_PolygonIndex;float m_DeltaZA;float m_DettaZB;AET *next;AET();~AET();
};

2.定义我们的扫描线算法的主类

class ScanLine
{public:Point pt[MAX_POINT];//存储所有点的信息int polygon[MAX_POINT][3];//我们的图形是由三角形组成的,保存三角形的顶点编号int currentPoint;//当前的点的数量int currentPolygon;//当前三角形的数量int m_Ymin;//最大的y值int m_Ymax;//最小的y值int m_Map[MAX_WIDTH][MAX_LENGTH];//帧缓存int m_ZB[MAX_WIDTH];//深度缓存Point color[MAX_POINT];ScanLine();~ScanLine();YTable *m_YTable[MAX_WIDTH];//多边形y表YTable *m_APT;//活化多边形表ET *m_ET[MAX_POINT][MAX_LENGTH];//边表AET *m_AET;//活化边对表void Draw();void InitData();//初始化信息,在这里读取我们的点和多边形void LoadObj();private:void CreatYTable();//创建y表void FindYminAndYmax();//设置m_Ymin和m_Ymax的值void CalculateNormal(int cntOfPolygon,float normal[]);
};

3.接下来就是按顺序执行我们的算法流程,首先读取obj文件到点和多边形里面
这里我的obj的读法比较简单,只考虑了点和面,发现和纹理等我都没有考虑。
值得注意的是,我们需要的坐标范围是1024*768
因此,当obj文件中的文件坐标过于小或者存在负的时候,需要自行对坐标进行方法以及平移处理
下面是放大100倍的情况,用于绘制后面的球,完整代码是不放大的情况,用来绘制海豚

if (cnt == 0)pt[currentPoint].x = (atof(strTemp))*100+1024/2;
else if (cnt == 1)pt[currentPoint].y = (atof(strTemp))*100+768/2;
void ScanLine::LoadObj()
{FILE *fp;int index;printf("请输入绘制的图形序号1:茶壶 2:海豚 3:球 4:花环\n");scanf("%d", &index);if(index == 1)fp = fopen("teapot.obj","r");else if(index ==2)fp = fopen("dolphins.obj", "r");else if (index == 3)fp = fopen("sphere.obj", "r");else if (index == 4)fp = fopen("torus.obj", "r");char str[100];char strTemp[100];int cnt;int indexOfstrTemp;if (!fp)printf("ERROR");else{while (fgets(str, 999, fp) != NULL){cnt = 0;indexOfstrTemp = 0;if (str[0] == 'v'&&str[1]!='n'&&str[1]!='t'){int i = 2;if (str[i] == ' ')i = 3;for (; ;i++)if (str[i] == ' '|| str[i]=='\0'){strTemp[indexOfstrTemp] = '\0';indexOfstrTemp = 0;if (cnt == 0)pt[currentPoint].x = (atof(strTemp))+1024/2;else if (cnt == 1)pt[currentPoint].y = (atof(strTemp))+768/2;else{pt[currentPoint].z = atof(strTemp);currentPoint++;}cnt = (cnt + 1) % 3;       if (str[i] == '\0')break;}else{strTemp[indexOfstrTemp++] = str[i];}}else if (str[0] == 'f'){for (int i = 0; i < strlen(str); i++)if (str[i] == ' ')cnt++;if (cnt == 3){cnt = 0;for (int i = 2; ; i++)if (str[i] == ' ' || str[i] == '\0'||str[i]=='\\'){if(str[i]=='\\')for (; str[i] != '\0'&&str[i] != ' '; i++);strTemp[indexOfstrTemp] = '\0';indexOfstrTemp = 0;polygon[currentPolygon][cnt] = atof(strTemp)-1;if (cnt == 2)currentPolygon++;cnt = (cnt + 1) % 3;if (str[i] == '\0')break;}else{strTemp[indexOfstrTemp++] = str[i];}}else if (cnt == 4){cnt = 0;for (int i = 2; ; i++)if (str[i] == ' ' || str[i] == '\0'){strTemp[indexOfstrTemp] = '\0';indexOfstrTemp = 0;if (cnt == 3){polygon[currentPolygon][0] = polygon[currentPolygon-1][0];polygon[currentPolygon][1] = polygon[currentPolygon-1][2];polygon[currentPolygon][2] = atof(strTemp)-1;currentPolygon++;}polygon[currentPolygon][cnt] = atof(strTemp)-1;if (cnt == 2)currentPolygon++;cnt++;if (str[i] == '\0')break;}else{strTemp[indexOfstrTemp++] = str[i];}}}}}
}

4.给我们的三角形随机颜色

void ScanLine::InitData()
{srand((int)time(NULL));for (int i = 0; i < currentPolygon; i++){color[i].x = rand() % 1024 / 1024.0;color[i].y = rand() % 1024 / 1024.0;color[i].z = rand() % 1024 / 1024.0;}
}

5.开始绘图函数
这里要初始化y表,然后找到最大和最小的y值

void ScanLine::CreatYTable()
{float ymin = 99999;float ymax = 0;for (int i = 0; i < currentPolygon; i++){//找到最低的顶点坐标ymin = 99999;ymax = 0; for (int j = 0; j < 3; j++){if (ymin > pt[polygon[i][j]].y)ymin = pt[polygon[i][j]].y;if (ymax < pt[polygon[i][j]].y)ymax = pt[polygon[i][j]].y;}//插入Y表int y = ceil(ymin);//如果在首位的话,直接插入if (m_YTable[y] == nullptr){m_YTable[y] = new YTable();m_YTable[y]->m_IndexOfPolygon = i;m_YTable[y]->m_Ymax = ymax;m_YTable[y]->next = nullptr;}//不在首位,则需要找到最后一位,然后插入到那个位置else{YTable *YTableTemp = m_YTable[y];while (YTableTemp->next != nullptr)YTableTemp = YTableTemp->next;YTable *YTableTemp1 = new YTable();YTableTemp1->m_IndexOfPolygon = i;YTableTemp1->m_Ymax = ymax;YTableTemp1->next = nullptr;YTableTemp->next = YTableTemp1;}}
}
void ScanLine::FindYminAndYmax()
{m_Ymax = 0;m_Ymin = 999999;for (int i = 0; i < currentPoint; i++){if (m_Ymax < pt[i].y)m_Ymax = ceil(pt[i].y);if (m_Ymin > pt[i].y)m_Ymin = ceil(pt[i].y);}
}

6.下面就是完整的绘图函数
基本上就是按照以下步骤来的
创建y表
找到y的最大值最小值
初始化帧缓存
循环每一条扫描线
初始化深度缓存
新建APT,ET,AET
遍历AET绘图
删除到达顶点的APT,AET
更新AET

void ScanLine::Draw()
{//建立Y表CreatYTable();//找到最大的y和最小的y值,减小扫描线的数量FindYminAndYmax();//初始化背景颜色memset(m_Map, 0, sizeof(m_Map));//遍历所有的扫描线for (int i = m_Ymin; i <= m_Ymax; i++){//初始化深度缓存fill(m_ZB, m_ZB + MAX_WIDTH, -99999);//如果y表中有扫描线i对应的扫描线,则将其加入到APT中if (m_YTable[i] != nullptr){YTable *headOfYtable = m_YTable[i];YTable *headOfAPT = m_APT;if(headOfAPT!=nullptr)while (headOfAPT->next != nullptr)headOfAPT = headOfAPT->next;//将y表中多边形依次插入到APT中,并为其建立边表while (headOfYtable != nullptr){YTable *YTableTemp = new YTable(headOfYtable->m_IndexOfPolygon, headOfYtable->m_Ymax);YTableTemp->next = nullptr;if (m_APT == nullptr){m_APT = YTableTemp;headOfAPT = m_APT;}else{headOfAPT->next = YTableTemp;headOfAPT = headOfAPT->next;}//对于刚加入的多边形,生成其边表for (int j = 0; j < 3; j++){//三角形,三条边进行遍历if (ceil(pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y) < ceil(pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j+1)%3]].y)){ET *newET = new ET();newET->next = nullptr;newET->m_Ymax = pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y;newET->m_Yminx = pt[polygon[headOfYtable->m_IndexOfPolygon][j]].x;newET->m_Yminz = pt[polygon[headOfYtable->m_IndexOfPolygon][j]].z;newET->m_DeltaX = (pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].x - pt[polygon[headOfYtable->m_IndexOfPolygon][j]].x) / ceil((pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y) - ceil(pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y));if(m_ET[headOfYtable->m_IndexOfPolygon][(int)ceil(pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y)]==nullptr)m_ET[headOfYtable->m_IndexOfPolygon][(int)ceil(pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y)] = newET;elsem_ET[headOfYtable->m_IndexOfPolygon][(int)ceil(pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y)]->next = newET;                 }else if(ceil(pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y) > ceil(pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y)){ET *headOfET = m_ET[headOfYtable->m_IndexOfPolygon][(int)ceil(pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y)];ET *newET = new ET();newET->next = nullptr;newET->m_Ymax = pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y;newET->m_Yminx = pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].x;newET->m_Yminz = pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].z;newET->m_DeltaX = (pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].x - pt[polygon[headOfYtable->m_IndexOfPolygon][j]].x) / ceil((pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y) - ceil(pt[polygon[headOfYtable->m_IndexOfPolygon][j]].y));if (m_ET[headOfYtable->m_IndexOfPolygon][(int)ceil(pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y)] == nullptr)m_ET[headOfYtable->m_IndexOfPolygon][(int)ceil(pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y)] = newET;elsem_ET[headOfYtable->m_IndexOfPolygon][(int)ceil(pt[polygon[(headOfYtable->m_IndexOfPolygon)][(j + 1) % 3]].y)]->next = newET;}}headOfYtable = headOfYtable->next;}//遍历APT中的多边形,将其边表中对应的边对加入活化边对表AETheadOfAPT = m_APT;while (headOfAPT != nullptr){if (m_ET[headOfAPT->m_IndexOfPolygon][i] != nullptr){//因为我们只考虑三角形,多边形没考虑,这里只需要考虑一个边和两个边的时候if (m_ET[headOfAPT->m_IndexOfPolygon][i]->next != nullptr){ET edge1;ET edge2;AET *newAET = new AET();//确定左边的边是哪一个edge1 = *m_ET[headOfAPT->m_IndexOfPolygon][i];edge2 = *m_ET[headOfAPT->m_IndexOfPolygon][i]->next;if (edge1.m_Yminx > edge2.m_Yminx){ET temp;temp = edge1;edge1 = edge2;edge2 = temp;}else if(edge1.m_DeltaX > edge2.m_DeltaX){ET temp;temp = edge1;edge1 = edge2;edge2 = temp;}newAET->m_DeltaXL = edge1.m_DeltaX;newAET->m_DeltaXR = edge2.m_DeltaX;newAET->m_XL = edge1.m_Yminx;newAET->m_XR = edge2.m_Yminx;newAET->m_ZL = edge1.m_Yminz;newAET->m_Lmax = edge1.m_Ymax;newAET->m_Rmax = edge2.m_Ymax;newAET->m_PolygonIndex = headOfAPT->m_IndexOfPolygon;newAET->next = nullptr;//计算出多边形的法向量float normal[3];CalculateNormal(newAET->m_PolygonIndex, normal);newAET->m_DeltaZA = -normal[0] / normal[2];newAET->m_DettaZB = -normal[1] / normal[2];//插入aetif (m_AET == nullptr)m_AET = newAET;else{AET *headOfAET = m_AET;while (headOfAET->next != nullptr)headOfAET = headOfAET->next;headOfAET->next = newAET;}}}headOfAPT = headOfAPT->next;}}//遍历AET画图AET *headOfAET = m_AET;while (headOfAET != nullptr){if(headOfAET->m_XL>=0&&headOfAET->m_XL<=1024&& headOfAET->m_XR>=0&& headOfAET->m_XR<=1024)for (int j = ceil(headOfAET->m_XL); j <= ceil(headOfAET->m_XR) && j<=1024; j++){if (headOfAET->m_ZL + headOfAET->m_DeltaZA * (j - ceil(headOfAET->m_XL)) > m_ZB[j]){m_ZB[j] = headOfAET->m_ZL + headOfAET->m_DeltaZA * (j - ceil(headOfAET->m_XL));m_Map[j][i] = headOfAET->m_PolygonIndex+1;}}headOfAET = headOfAET->next;}//删除APT中最大顶点y坐标为i的多边形YTable *headOfAPT = m_APT;//先删除是首元素的情况while (headOfAPT != nullptr &&headOfAPT->m_Ymax <= i){m_APT = headOfAPT->next;free(headOfAPT);headOfAPT = m_APT;}while (headOfAPT!=nullptr&&headOfAPT->next != nullptr){//如果到达顶点,则删除这个多边形if (headOfAPT->next->m_Ymax <= i){YTable *next = headOfAPT->next;headOfAPT->next = next->next;free(next);}elseheadOfAPT = headOfAPT->next;}//更新AET中的每一条边对//删除边对中到达ymax的边headOfAET = m_AET;//先删除是首元素的情况while (headOfAET != nullptr && headOfAET->m_Lmax <= i &&headOfAET->m_Rmax <= i){m_AET = headOfAET->next;free(headOfAET);headOfAET = m_AET;}//删除后面到达顶点的while (headOfAET!=nullptr&&headOfAET->next != nullptr){if (headOfAET->next->m_Lmax <= i && headOfAET->next->m_Rmax <= i){AET *next = headOfAET->next;headOfAET->next = next->next;free(next);}elseheadOfAET = headOfAET->next;}headOfAET = m_AET;//更新后面的值while (headOfAET != nullptr){//只删除一条边,则需要补一条边if (headOfAET->m_Lmax <= i){if (m_ET[headOfAET->m_PolygonIndex][i] != nullptr){headOfAET->m_Lmax = m_ET[headOfAET->m_PolygonIndex][i]->m_Ymax;headOfAET->m_DeltaXL = m_ET[headOfAET->m_PolygonIndex][i]->m_DeltaX;headOfAET->m_ZL = m_ET[headOfAET->m_PolygonIndex][i]->m_Yminz;headOfAET->m_XL = m_ET[headOfAET->m_PolygonIndex][i]->m_Yminx;}}else if (headOfAET->m_Rmax <= i){if (m_ET[headOfAET->m_PolygonIndex][i] != nullptr){headOfAET->m_Rmax = m_ET[headOfAET->m_PolygonIndex][i]->m_Ymax;headOfAET->m_DeltaXR = m_ET[headOfAET->m_PolygonIndex][i]->m_DeltaX;headOfAET->m_XR = m_ET[headOfAET->m_PolygonIndex][i]->m_Yminx;}}//更新headOfAET->m_XL += headOfAET->m_DeltaXL;headOfAET->m_XR += headOfAET->m_DeltaXR;headOfAET->m_ZL += headOfAET->m_DeltaXL*headOfAET->m_DeltaZA + headOfAET->m_DettaZB;headOfAET = headOfAET->next;}   }
}

7.主函数

#include <GLTools.h>
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLGeometryTransform.h>
#define FREEGLUT_STATIC
#include <glut.h>
#pragma comment(lib,"gltools.lib")
#include "ScanLine.h"
ScanLine scanline;
void DisPlayTest()
{glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);//设置混合模式glEnable(GL_BLEND);//开启混合glEnable(GL_POINT_SMOOTH);//点的抗锯齿glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);//最好模式运行平滑 可选GL_FASTESTglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);glBegin(GL_POINTS);for (int i = 0; i<1024; i++)for (int j = 0; j < 768; j++){if (scanline.m_Map[i][j] != 0){glColor3f(scanline.color[scanline.m_Map[i][j]-1].x,scanline.color[scanline.m_Map[i][j] - 1].y,scanline.color[scanline.m_Map[i][j] - 1].z);glVertex2f(i/ 1024.0*2 -1,j/ 768.0*2 -1);}}glEnd();glFlush();
}int main(int argc, char* argv[])
{scanline.LoadObj();scanline.InitData();scanline.Draw();glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);glutInitWindowSize(1024, 768);glutCreateWindow("GL_POINTS");glutDisplayFunc(DisPlayTest);glutMainLoop();return 0;
}

效果图如下:



计算机图形学常用算法实现11 扫描线z-buffer算法相关推荐

  1. 计算机图形学常见算法原理,计算机图形学常用算法及代码大全

    <计算机图形学常用算法及代码大全>由会员分享,可在线阅读,更多相关<计算机图形学常用算法及代码大全(41页珍藏版)>请在人人文库网上搜索. 1.2.1.1 生成直线的DDA算法 ...

  2. line划线计算机图像学,【计算机图形学】根本图形元素:直线的生成算法

    [计算机图形学]基本图形元素:直线的生成算法 08年9月入学,12年7月毕业,结束了我在软件学院愉快丰富的大学生活.此系列是对四年专业课程学习的回顾,索引参见:http://blog.csdn.net ...

  3. c语言计算机图形来画八分画圆,【计算机图形学】基本图形元素:圆的生成算法...

    08年9月入学,12年7月毕业,结束了我在软件学院愉快丰富的大学生活.此系列是对四年专业课程学习的回顾,索引参见:http://blog.csdn.net/xiaowei_cqu/article/de ...

  4. 计算机图形学+简单算法实现,《计算机图形学》课程设计-简单几何体的消隐算法实现.doc...

    PAGE PAGE 18 PAGE 18 课程名称:<计算机图形学> 论文题目:简单几何体的消隐算法实现 教学部: 年 级: 班 级: 学 号: 姓 名: 简单几何体的消隐算法实现 摘 要 ...

  5. 【计算机图形学】基本图形元素:直线的生成算法

    08年9月入学,12年7月毕业,结束了我在软件学院愉快丰富的大学生活.此系列是对四年专业课程学习的回顾,索引参见:http://blog.csdn.net/xiaowei_cqu/article/de ...

  6. 多边形区域填充算法--扫描线种子填充算法

    分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow http://bl ...

  7. 计算机图形学常用算法

    http://blog.csdn.net/orbit/article/details/7082678 http://dev.gameres.com/Program/Abstract/Geometry. ...

  8. 计算机图形学常用算法实现9 梁友栋-Barskey裁剪算法

    这个算法的效率比前面提到的Cohen-Sutherland要高 思路是把直线表示为参数方程形式, x= x1+udx y = y1+udy 由xmin<x<xmax ymin<y&l ...

  9. 计算机图形学常用算法实现10 多边形裁剪Sutherland-Hodgman算法

    算法原理比较简单,用裁剪区域的四条边分别去分割多边形. 假设边p1,p2,用区域某条边进行裁剪.方向为p1->p2 1.p1在边外,p2在边外 无操作 2.p1外,p2内 保存交点p和p2 3. ...

最新文章

  1. html5 键盘触发事件
  2. 隐藏通知内容什么意思_降额来袭!信用卡风控短信背后,的“隐藏暗语”你懂吗?...
  3. docker学习笔记-为容器配置重启策略
  4. 【快乐水题】219. 存在重复元素 II
  5. Python3之configparser模块
  6. 注意力机制可视化_目标跟踪中的(STAM)时空注意力机制
  7. unity如何让物体与特定物体之间不发生碰撞
  8. 轻操作动作休闲单机游戏《狂斩三国2》评测
  9. opencv mat数据剪裁感兴趣的部分处理方法
  10. 【今日CV 计算机视觉论文速览】Thu, 28 Feb 2019
  11. Android 百度地图开发问题----解决地图有时候加载不出来问题
  12. shell信息查看脚本linux,每次登录Shell时使用shell脚本查看Linux上的系统信息
  13. 真正支配整个世界的十种算法
  14. 博科交换机zone划分
  15. HTML-DOM零碎
  16. centos nginx php_Centos7下NGINX+PHP的安装及配置
  17. openCV无法打开源文件opencv2\opencv.hpp
  18. Axure RP 8--模板的使用
  19. vcm驱动芯片原理_手机Cam和era模组及VCM与VCMDriver介绍.pptx
  20. 用python根据年份判断生肖_C#中根据年份判断十二生肖

热门文章

  1. 遗传算法 商旅问题 c++ GA tsp
  2. linux项目部署、Nginx详解
  3. 北京圣思园张龙Java教学视频学习笔记1
  4. Matlab透视变换
  5. 学习大数据前应该了解什么?
  6. vscode中检查单词是否拼写错误的插件
  7. java集合set不能去重_java集合去重和排序
  8. Windows 10 64bit 安装dotnetfx 3.5出错的解决办法(备忘)
  9. 基于V4L2的视频驱动开发(2) 华清远见 刘洪涛
  10. Android之vold进程启动源码分析