标签:tor err objc inf 学习 关闭 sns like ora
本片blog阐述了图形学中扫描线缓冲器消隐算法的原理和C++实现。
题外话:上学期上过冯结青老师的课,首先为老师的认真态度点赞,不但课件做得好。课也上得好,讲课条理清晰。难易适中。是我上过的10来门课中讲得最好,留下印象最深的。
以上图片的顺序是从左到右,从上到下。
首先准备须要的数据结构:
1.分类多边形的边表结点:
typedef struct nodeClassifiedPolygon{ GLdouble a, b, c, d;//多边形系数 GLint id;//多边形编号 GLint dy;//跨越的扫描线数目 Triple<GLubyte> color;//多边形颜色 nodeClassifiedPolygon* next;//同一扫描线上的下一个多边形 }nodeClassifiedPolygon;2.分类边表的边表结点:
typedef struct nodeClassifiedEdge{ GLdouble x;//边上端点的x坐标 GLdouble dx;//扫描线向下移动一次时x的增量 GLint dy;//边跨越的扫描线数目 GLint id;//边所在所变形的编号 bool used;//该边是否已经处理过 nodeClassifiedEdge* next;//具有同样x的下一条边 }nodeClassifiedEdge;3.活化多边形链表结点:(结构与nodeClassifiedEdge同样):
typedef struct nodeActivePolygon{ GLdouble a, b, c, d; GLint id; GLint dy; Triple<GLubyte> color; nodeActivePolygon* next; }nodeActivePolygon;4.活化边对链表结点:
typedef struct nodeActiveEdgePair{ GLdouble xl;//当前扫描线边对的左临界点 GLdouble dxl;//相邻扫描线交点x坐标之差 GLint dyl;//靠左的边跨越的扫描线数目 GLdouble xr; //当前扫描线边对的右临界点 GLdouble dxr;//扫描线向下移动一次时右边的xr的增量 GLint dyr;//靠右的边跨越的扫描线数目 GLdouble zl;//当前扫描线下左边的深度 GLdouble dzx;//向右扫描一个像素时,深度的增量 GLdouble dzy;//向下移动扫描线时深度的增量 GLint id;//边对所在多边形的编号 Triple<GLubyte> color;//颜色 nodeActiveEdgePair* next;//下一边对 }nodeActiveEdgePair;
</pre><span style="font-family:Microsoft YaHei;font-size:18px;"></span><p><span style="white-space:pre"> </span><span style="font-family:Microsoft YaHei;font-size:18px;">5.分类边表的边表vector:</span></p><p><pre name="code" class="cpp">vector<nodeClassifiedPolygon*> tClassifiedPolygon;
6.分类多边形的边表vector
vector<nodeClassifiedEdge*> tClassifiedEdge;
7.活化多边形链表的头指针。该指针的next指向第一个节点:
nodeActivePolygon tActivePolygonHead;
8.活化边对链表的头指针,该指针的next指向第一个节点:
nodeActiveEdgePair tActiveEdgePairHead;
9.使用了例如以下额外的数据结构:
typedef struct{ GLint x,y; GLdouble z; } Point;这个结构的作用是在读取obj模型时,模型的三维定点坐标都是浮点数,而帧缓存的像素坐标是整数。可是z坐标,即深度值仍然能够是浮点。因此在消隐的时候将会把一个xyz坐标都是浮点的顶点转化为Point结构。当中,转化的结果是在xOy面上取近期点。
1.首先定义一个三元组模板类,这在顶点坐标和颜色值都会用到:
#pragma once template <typename T> class Triple { public: T x, y, z; Triple(T _x, T _y, T _z):x(_x), y(_y), z(_z){} Triple(T v[3]):x(v[0]), y(v[1]), z(v[2]){} Triple(){} ~Triple(void){} void operator = (const Triple<T>& a){ x = a.x; y = a.y; z = a.z; } Triple<T> operator + (const Triple<T>& a/*, Point b*/){ return Triple<T>(x+a.x, y+a.y, z+a.z); } Triple<T> operator + (GLdouble a/*, Point b*/){ return Triple<T>(x+a, y+a, z+a); } Triple<T> operator * (GLdouble a/*, Point b*/){ return Triple<T>(x*a, y*a, z*a); } Triple<T> operator - (const Triple<T>& a/*, Point b*/){ return Triple<T>(x-a.x, y-a.y, z-a.z); } bool operator == (const Triple<T>& a/*, Point b*/){ return (x==a.x&&y==a.y&&z==a.z); } bool operator != (const Triple<T>& a/*, Point b*/){ return !((*this)==a); } };
#include "Triple.h" class FrameBuffer{ public: FrameBuffer():m_width(0),m_height(0)/*,m_centerX(0),m_centerY(0),m_center(0)*/{} //----更新高度和宽度,并又一次设置其大小 void ResizeBuffer(int width,int height){ if(m_width!=width || m_height!=height){ m_width = width; m_height = height; m_buffer.clear(); m_buffer.resize(m_width * m_height * 3); // 帧缓存大小 //m_centerX = m_width >> 1; //m_centerY = m_height >> 1; //m_center = m_width * m_centerY + m_centerX; } } //---初使化帧缓存---- void Memset(int value){ memset(&m_buffer[0],value,m_buffer.size()); } //---返回要写入的帧缓存位置----- void SetPixel(int x,int y,Triple<GLubyte>& color){ if(x<0 || x>=m_width || y <0 || y>=m_height) return; *(&m_buffer[0]+(m_width*y+x)*3) = color.x; *(&m_buffer[0]+(m_width*y+x)*3+1) = color.y; *(&m_buffer[0]+(m_width*y+x)*3+2) = color.z; //std::cout<<"x:"<<x<<" y:"<<y<<std::endl; //memcpy(&m_buffer[m_center]+(m_width*y+x)*3, &color[0], 3);//debugger //} } Triple<GLubyte>* getPixelAddr(int x,int y){ if(y>=0 && y<m_height && x>=0 && x<m_width) return ((Triple<GLubyte>*)(&m_buffer[0]+(m_width*y+x)*3)); std::cout<<"getPixelAddr error"<<std::endl; return NULL; } int getBufferWidth(){ return m_width; } int getBufferHeight(){ return m_height; } public: std::vector<GLubyte> m_buffer; // 帧缓存 int m_width,m_height; // 宽度/高度 //int m_centerX, m_centerY, m_center; };
#pragma once #include <fstream> #include <string> #include <Windows.h> #include <windef.h> #include <io.h> #include "Triple.h" #include "Util.h" class Obj { public: std::vector<Triple<GLdouble>> vVertex; //std::vector<Triple<GLdouble>> vVertexErr;//误差修正 //GLdouble m_errAccuracy;//误差修正精度 GLdouble m_offsetX, m_offsetY, m_offset; GLdouble m_scaleFactor; std::vector<std::vector<Triple<GLdouble>>> vfs;//面 std::vector<std::vector<GLint>> ifaces; //vector<vector<string>> model; std::string objpath; GLint nVertex;//顶点数 GLint nFace;//面数 GLint m_winWidth, m_winHeight; public: Obj(){ m_winWidth = 1600,m_winHeight = 900; string moduleDir = getModueDir(); vector<string> files; //cout<<"moduleDir:"<<moduleDir<<endl; getFiles(moduleDir, files); int item = getItem(files); objpath = files[item-1]; loadObjFile(); vfs.resize(nFace); } ~Obj(){} int getItem(vector<string>& files){ //cout<<"size:"<<files.size()<<endl; for(int i = 0; i <files.size(); i++) cout<<i+1<<"."<<files[i]<<endl; cout<<"chose one to perform:"; int j; cin>>j; while(j<=0||j>files.size()){ cout<<"error,again:"; cin>>j; } return j; } void getFiles(string path, vector<string>& files ){ //文件句柄 long hFile = 0; //文件信息 struct _finddata_t fileinfo; string p; if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) != -1) { do { //假设是文件夹,迭代之 //假设不是,增加列表 if((fileinfo.attrib & _A_SUBDIR)) { if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0) getFiles( p.assign(path).append("\\").append(fileinfo.name), files ); } else { files.push_back(p.assign(path).append("\\").append(fileinfo.name) ); } }while(_findnext(hFile, &fileinfo) == 0); _findclose(hFile); } } string getModueDir(){ string exe = getEXEDir(); int pos = exe.find_last_of("\\\\"); pos = exe.substr(0,pos).find_last_of("\\\\"); if(pos > 0){ return exe.substr(0,pos) + "\\\\model"; } return ""; } string getEXEDir(){ TCHAR exeFullPath[MAX_PATH]; // MAX_PATH在WINDEF.h中定义了,等于260 memset(exeFullPath,0,MAX_PATH); GetModuleFileName(NULL,exeFullPath,MAX_PATH); return string(exeFullPath); } void loadObjFile(){ /*第一遍扫描记录xy坐标的最小值和最大值,有例如以下两个作用: 1.假设这两个值跨度太小,说明模型太小。为了显示模型,须要对坐标值进行放大 2.对原始坐标进行偏移,使得模型基本能够全然展示出来*/ GLdouble min_x = 999999.99, max_x = -999999.99; GLdouble min_y = 999999.99, max_y = -999999.99; Util util; std::ifstream ifs(objpath); std::string line; vVertex.push_back(Triple<GLdouble>(0,0,0)); while(getline(ifs,line)){ vector<string> vline; //model[i].clear(); util.split(vline, line); if(vline.size() == 0) continue; if("v" == vline[0]){ //_ASSERT(vline.size() > 3); std::string s; GLdouble vertex[3]; GLdouble d;//当前读取的坐标值 for(int i = 0; i < 3; i++){ s = vline[i+1]; sscanf_s(s.c_str(), "%lf", &vertex[i]); } if(vertex[0] < min_x) min_x = vertex[0]; if(vertex[0] > max_x) max_x = vertex[0]; if(vertex[1] < min_y) min_y = vertex[1]; if(vertex[1] > max_y) max_y = vertex[1]; vVertex.push_back(Triple<GLdouble>(vertex)); //vVertexErr.push_back(Triple<GLdouble>(0.0,0.0,0.0)); }else if("f" == vline[0]){ //_ASSERT(vline.size() > 3); std::vector<GLint> ele; int d; std::string s; for(GLuint i = 0; i < 4 && (i+1) < vline.size(); i++){ s = vline[i+1]; //丢弃材质等信息 int slash_pos = s.find_first_of(‘/‘); if(slash_pos >= 0) s = s.substr(0, slash_pos); //...不支持负的顶点索引 sscanf_s(s.c_str(), "%d", &d); ele.push_back(d); } ifaces.push_back(ele); } } //offset //m_offset = m_offsetX > m_offsetY ? m_offsetX : m_offsetY; //scale GLdouble spanx = max_x - min_x, spany = max_y - min_y; //GLdouble span = (spanx<spany?spanx:spany); //m_scaleFactor = (m_winHeight*100) / span / span; if(spanx <= 1 || spany <= 1) m_scaleFactor = 1000; else if(spanx < 5 || spany < 5) m_scaleFactor = 80; else if(spanx < 10 || spany < 10) m_scaleFactor = 40; else if(spanx < 20 || spany < 20) m_scaleFactor = 20; else if(spanx < 40 || spany < 40) m_scaleFactor = 10; else m_scaleFactor = 1; m_offsetX = (m_winWidth >> 1) - ((max_x + min_x)*m_scaleFactor / 2);//abs(min_x) + 0.5; m_offsetY = (m_winHeight >> 1) - ((max_y + min_y)*m_scaleFactor / 2); nVertex = vVertex.size() - 1; nFace = ifaces.size(); //model.clear(); ifs.close(); } Obj& getObj(){ translateObj(Triple<GLdouble>(0,0,0)); return *this; }; void Matrix_Multip(GLdouble *A_matrix,GLdouble *B_matrix,GLdouble *AB_matrix, int A_Rows,int A_Colunms,int B_Rows,int B_Colunms) { //矩阵乘法 double*AB_matrix最后的结果 for (int i=0;i<A_Rows;i++) { for (int j=0;j<B_Colunms;j++) { AB_matrix[i*B_Colunms+j]=0.0; for (int k=0;k<A_Colunms;k++) { AB_matrix[i*B_Colunms+j]=AB_matrix[i*B_Colunms+j]+A_matrix[i*A_Colunms+k]*B_matrix[k*B_Colunms+j]; } } } } Obj& rotateObj(GLdouble angle, Triple<GLdouble>& p/*1, Triple<GLdouble>& p2*/){ GLdouble a = 1.0;//rotVec.x / modRotateAxis; GLdouble b = 1.0;//rotVec.y / modRotateAxis; GLdouble c = 1.0;//rotVec.z / modRotateAxis; //GLdouble d = sqrt(b*b+c*c); GLdouble theta = 3.1415926536*angle/180; GLdouble sintheta = sin(theta); GLdouble costheta = cos(theta); GLdouble transMat[4][4] = { 1.0,0.0,0.0,m_offsetX, 0.0,1.0,0.0,m_offsetY, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0 }; GLdouble scaleMat[4][4] = { m_scaleFactor,0.0,0.0,0.0, 0.0,m_scaleFactor,0.0,0.0, 0.0,0.0,m_scaleFactor,0.0, 0.0,0.0,0.0,1.0 }; GLdouble rotateMat[4][4] = { (1-costheta)*a*a+costheta, (1-costheta)*b*a-sintheta*c, (1-costheta)*a*c+sintheta*b,0, (1-costheta)*a*b+sintheta*c,(1-costheta)*b*b+costheta, (1-costheta)*b*c-sintheta*a,0, (1-costheta)*a*c-sintheta*b,(1-costheta)*b*c+sintheta*a, (1-costheta)*c*c+costheta, 0, 0, 0, 0, 1 }; GLdouble* vertices = (GLdouble*)malloc(sizeof(GLdouble)*4*nVertex);//减1是因为顶点数组第一个顶点是占位元素 for(int i = 0; i < nVertex; i++){ *(vertices + i) = vVertex[i+1].x/* + vVertexErr[i+1].x*/; *(vertices + i + nVertex) = vVertex[i+1].y/* + vVertexErr[i+1].y*/; *(vertices + i + (nVertex<<1)) = vVertex[i+1].z/* + vVertexErr[i+1].z*/; *(vertices + i + ((nVertex<<1)+nVertex)) = 1.0; } GLdouble* res = (GLdouble*)malloc(sizeof(GLdouble)*4*nVertex); //GLdouble* res2 = (GLdouble*)malloc(sizeof(GLdouble)*4*nVertex); //先旋转 Matrix_Multip(reinterpret_cast<GLdouble*>(rotateMat), vertices, res, 4, 4, 4, nVertex); //原始数据保存 for(int i = 0; i < nVertex; i++){ vVertex[i+1].x = *(res + i)/* + (*(res + i)>=0?0.5:-0.5)*/;//大于0向上取整 vVertex[i+1].y = *(res + i + nVertex)/* + (*(res + i + nVertex)>=0?0.5:-0.5)*/; vVertex[i+1].z = *(res + i + (nVertex<<1))/* + (*(res + i + (nVertex<<1))>=0?0.5:-0.5)*/; } //再缩放 Matrix_Multip(reinterpret_cast<GLdouble*>(scaleMat), res, vertices, 4, 4, 4, nVertex); //平移至屏幕中心 Matrix_Multip(reinterpret_cast<GLdouble*>(transMat), vertices, res, 4, 4, 4, nVertex); for(GLuint i = 0; i < nFace; i++){ vfs[i].clear(); for(GLuint j = 0; j < ifaces[i].size(); j++) vfs[i].push_back(Triple<GLdouble>(*(res + ifaces[i][j]-1), *(res + ifaces[i][j]-1 + nVertex), *(res + ifaces[i][j]-1 + (nVertex<<1)))); } free(vertices); free(res); //free(res2); return *this; } Obj& translateObj(Triple<GLdouble>& p = Triple<GLdouble>(0,0,0)){ //GLdouble transX,transY; m_offsetX = m_offsetX + p.x; m_offsetY = m_offsetY + p.y; GLdouble transMat[4][4] = { 1.0,0.0,0.0,m_offsetX, 0.0,1.0,0.0,m_offsetY, 0.0,0.0,1.0,0, 0.0,0.0,0,1.0 }; GLdouble scaleMat[4][4] = { m_scaleFactor,0.0,0.0,0.0, 0.0,m_scaleFactor,0.0,0.0, 0.0,0.0,m_scaleFactor,0.0, 0.0,0.0,0.0,1.0 }; GLdouble* vertices = (GLdouble*)malloc(sizeof(GLdouble)*4*nVertex); for(int i = 0; i < nVertex; i++){ *(vertices + i) = vVertex[i+1].x; *(vertices + i + nVertex) = vVertex[i+1].y; *(vertices + i + (nVertex<<1)) = vVertex[i+1].z; *(vertices + i + ((nVertex<<1)+nVertex)) = 1.0; } GLdouble* res = (GLdouble*)malloc(sizeof(GLdouble)*4*nVertex); //先缩放 Matrix_Multip(reinterpret_cast<GLdouble*>(scaleMat), vertices, res, 4, 4, 4, nVertex); //再平移 Matrix_Multip(reinterpret_cast<GLdouble*>(transMat), res, vertices, 4, 4, 4, nVertex); //vfs.clear(); vfs.resize(nFace); for(GLuint i = 0; i < nFace; i++){ vfs[i].clear(); for(GLuint j = 0; j < ifaces[i].size(); j++) vfs[i].push_back(Triple<GLdouble>(*(vertices + ifaces[i][j]-1),//x *(vertices + ifaces[i][j]-1 + nVertex),//y *(vertices + ifaces[i][j]-1 + (nVertex<<1))));//z } free(vertices); free(res); return *this; } Obj& scaleObj(Triple<GLdouble>& p1, Triple<GLdouble>& p2, bool smaller){ //double _scaleFactor = 0.0; if(smaller) m_scaleFactor = m_winHeight/abs(p1.y-p2.y + m_winHeight); else m_scaleFactor = abs(p1.y-p2.y + m_winHeight)/m_winHeight; GLdouble scaleMat[4][4] = { m_scaleFactor,0,0,0, 0,m_scaleFactor,0,0, 0,0,m_scaleFactor,0, 0,0,0,1 }; GLdouble transMat[4][4] = { 1.0,0.0,0.0,m_offsetX, 0.0,1.0,0.0,m_offsetY, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0 }; GLdouble* vertices = (GLdouble*)malloc(sizeof(GLdouble)*4*nVertex); for(int i = 0; i < nVertex; i++){ *(vertices + i) = vVertex[i+1].x + 0.0; *(vertices + i + nVertex) = vVertex[i+1].y + 0.0; *(vertices + i + (nVertex<<1)) = vVertex[i+1].z + 0.0; *(vertices + i + ((nVertex<<1)+nVertex)) = 1.0; } GLdouble* res = (GLdouble*)malloc(sizeof(GLdouble)*4*nVertex); //放缩 Matrix_Multip(reinterpret_cast<GLdouble*>(scaleMat), vertices, res, 4, 4, 4, nVertex); //保存原始数据 for(int i = 0; i < nVertex; i++){ vVertex[i+1].x = *(res + i); vVertex[i+1].y = *(res + i + nVertex); vVertex[i+1].z = *(res + i + (nVertex<<1)); } //平移 Matrix_Multip(reinterpret_cast<GLdouble*>(transMat), res, vertices, 4, 4, 4, nVertex); for(GLuint i = 0; i < nFace; i++){ vfs[i].clear(); for(GLuint j = 0; j < ifaces[i].size(); j++) vfs[i].push_back(Triple<GLdouble>(*(vertices + ifaces[i][j]-1), *(vertices + ifaces[i][j]-1 + nVertex), *(vertices + ifaces[i][j]-1 + (nVertex<<1)))); } free(vertices); free(res); return *this; } };构造Obj类时首先须要读入模型文件。并将读入的顶点和表示面的顶点索引分别保存在nVertex和ifaces中。
另外,在读入模型的时候须要确定模型的放缩倍数和平移量,以便在屏幕合适的位置用合适的大小将模型的消隐结果展示出来。
模型须要完毕3个功能:平移,旋转和缩放。这三个功能都是为了可以更好地观察结果。
旋转方法rotateObj的參数是旋转角度和旋转轴。
在整个project中,我都用最原始的数据来进行计算。每一次旋转和缩放后都会保存对原始数据的操作结果,平移不会保存,这样是为了将变换产生的精度损失降到最低。
4.显示回调的主体框架例如以下:
void display(void) { <span style="white-space:pre"> </span>glClear(GL_COLOR_BUFFER_BIT/* | GL_DEPTH_BUFFER_BIT*/); //glDrawBuffer(GL_FRONT_AND_BACK); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); frameBuffer.Memset(g_bgColor.x); getFrameBuffer(); glRasterPos2i(0,0); glDrawPixels(frameBuffer.getBufferWidth(), frameBuffer.getBufferHeight(), GL_RGB, GL_UNSIGNED_BYTE, frameBuffer.getPixelAddr(0,0)); glutSwapBuffers(); displayInfo(); }getFrameBuffer的主要任务就是用zbuffer消隐来写帧缓存对象frameBuffer,写完后再draw一下就perfect。
displayInfo是在控制台上显示一些执行信息用的。
5.getFrameBuffer
void getFrameBuffer(){ vector<face> vface; g_polygon_id = 0; if(bRotate){ vface = obj.getObj().rotateObj(1,Triple<GLdouble>(1,1,1)).vfs; } else vface = obj.getObj().vfs; constructDS(vface); zbuffer(); clearData(); }先依据是否须要旋转来获得各个面的顶点数据,然后constructDS构建消隐须要的tClassifiedPolygon、tClassifiedEdge。
消隐结束后清空数据,以免对下一次消隐产生副作用。
6.constructDS
void constructDS(vector<face>& vface){ //construct classified polygon and edge table //edge table for(GLuint i = 0; i < vface.size(); i++){ g_polygon_id++;//每一个面不管绘制与否都要编号,防止编号混乱 //if(g_polygon_id == 129) // g_polygon_id =g_polygon_id; //cout<<g_polygon_id<<endl; //if(!preProcessf(vface[i])) continue; //依次解决每一个面 vector<GLdouble> coffs(4); solveFaceCoffs(coffs, vface[i]); if(coffs[2] < 1e-8 && coffs[2] > -1e-8) continue;//垂直于投影xOy面的面不考虑 int polygon_maxy = (vface[i][0].y>0?vface[i][0].y:0), polygon_miny = polygon_maxy; for(GLuint j = 0; j < vface[i].size(); j++){ //依次解决每一个面的每条边 Point a = roundVertex(vface[i][j]); Point b = roundVertex(vface[i][(j+1)%vface[i].size()]);//%--最后一个点和第一个点也能构成边 //a.x += if(a.y < b.y){//让a成为当前处理边的上顶点 Point t = a; a = b; b = t; } if(a.y < 0) continue;//水平线下的边不考虑 if(a.y - b.y < 1e-8 && a.y - b.y > -1e-8) continue;//水平边不考虑(不影响结果)。由于绘制非水平边的时候已经绘制了水平边 //构造分类边表结点 nodeClassifiedEdge* pCE = (nodeClassifiedEdge*)malloc(sizeof(nodeClassifiedEdge)); pCE->x = a.x; pCE->dx = (b.x - a.x + .0) / (a.y - b.y);//相邻两条扫描线交点的x坐标差dx (-1/k) pCE->dy = a.y - b.y + 1; pCE->id = g_polygon_id; pCE->used = false; pCE->next = NULL; //增加分类边表 if(a.y >= tClassifiedEdge.size()) continue; if(tClassifiedEdge[a.y] == NULL) { tClassifiedEdge[a.y] = pCE; }else{ nodeClassifiedEdge* p = tClassifiedEdge[a.y]; while(p->next) p = p->next; p->next = pCE; } if(a.y > polygon_maxy) polygon_maxy = a.y; if(b.y < polygon_miny) polygon_miny = b.y; }//end for(int j = 0;...) nodeClassifiedPolygon* pCP = (nodeClassifiedPolygon*)malloc(sizeof(nodeClassifiedPolygon)); pCP->a = coffs[0]; pCP->b = coffs[1]; pCP->c = coffs[2]; pCP->d = coffs[3]; pCP->dy = polygon_maxy - polygon_miny + 1; pCP->id = g_polygon_id; pCP->color = getPolygonColor(coffs); pCP->next = NULL; if(polygon_maxy >= tClassifiedPolygon.size()) continue; if(tClassifiedPolygon[polygon_maxy] == NULL){ tClassifiedPolygon[polygon_maxy] = pCP; }else{ nodeClassifiedPolygon* p = tClassifiedPolygon[polygon_maxy]; while(p->next) p = p->next; p->next = pCP; } } }感觉凝视语句写得比較清楚了?
7.重头戏,zbuffer
void zbuffer(){ tActivePolygonHead.next = NULL; tActiveEdgePairHead.next = NULL; //scan for(GLint y = g_winHeight-1; y >= 0; y--){ init_g_zbuffer();//初始深度缓存 activateNewPolygon(y);//加入新的多边形到活化多边形表 deepthUpdate(y);//增量式深度更新帧缓存 activeEdgeTableUpdate(y);//活化边表元素改动 activePolygonTableUpdate();//更新活化多边形表 }//end for(int i = 4;...) }8.activateNewPolygon
void activateNewPolygon(GLint y){ //std::cout<<y<<std::endl; if(y < 0 || y >= tClassifiedPolygon.size() ) return; if(tClassifiedPolygon[y]){ //将当前扫描线扫描到的全部多边形增加活化多边形表 nodeClassifiedPolygon* pCP = tClassifiedPolygon[y]; while(pCP){//1个多边形的处理,将一个多边形增加活化多边形表(垂直于xOy面的平面不处理) //if(pCP->c < 1e-6 && pCP->c > -1e-6){//再一次保证垂直于xOy面的平面不处理 // pCP = pCP->next; // continue; //} //假设平面不垂直于xOy面,则分配结点放入活化多边形表 nodeClassifiedPolygon *t_pCP = (nodeClassifiedPolygon*)malloc(sizeof(nodeClassifiedPolygon)); *t_pCP = *pCP; pCP = t_pCP;//pcp的next指针仍然保留指向分类多边形表的指针 t_pCP = pCP->next; pCP->next = NULL; nodeActivePolygon *pAP = tActivePolygonHead.next; if(!pAP) tActivePolygonHead.next = (nodeActivePolygon*)pCP; else{ while(pAP->next) pAP = pAP->next; pAP->next = (nodeActivePolygon*)pCP; } //if(tActivePolygonHead.next == NULL){ // tActivePolygonHead.next = (nodeActivePolygon*)pCP;//活化多边形表结点的结构和分类多边形表的结构同样 // tailAP = (nodeActivePolygon*)pCP; //}else{ // tailAP->next = (nodeActivePolygon*)pCP; // tailAP = tailAP->next; //} //多边形增加活化多边形表以后,其两条边增加活化边表 //nodeClassifiedEdge* pCE = tClassifiedEdge[i]; //while(pCE){ //分类边表结点结构转化为活化边表结点结构 //找到边对的左边和右边 nodeClassifiedEdge *l, *r;//此处可能出现bug findEdge(&l, y, pCP->id); findEdge(&r, y, pCP->id); if(!l || !r) { #ifdef DEBUGGER cout<<__LINE__<<":find a polygon, but not find it‘s corresponding l & r edges."<<endl; #endif nodeClassifiedPolygon* next_pCP = pCP->next; pCP->next = NULL; pCP = next_pCP; continue; } if(l->x > r->x || (l->x == r->x && l->dx > r->dx)){ nodeClassifiedEdge* _t = l; l = r; r = _t; } //l->used = true; r->used = true; //构造活化边表结点 nodeActiveEdgePair* pAEP = (nodeActiveEdgePair*)malloc(sizeof(nodeActiveEdgePair)); pAEP->xl = l->x; pAEP->dxl = l->dx; pAEP->dyl = l->dy; pAEP->xr = r->x; pAEP->dxr = r->dx; pAEP->dyr = r->dy; pAEP->zl = (- pCP->d - l->x * pCP->a - y * pCP->b) / pCP->c; pAEP->dzx = (- pCP->a) / pCP->c; pAEP->dzy = pCP->b / pCP->c; pAEP->id = l->id; pAEP->color = pCP->color; pAEP->next = NULL; //将活化边表结点增加活化边表 if(tActiveEdgePairHead.next == NULL){ tActiveEdgePairHead.next = pAEP; }else{ //find active edge table‘s tail nodeActiveEdgePair* _pAEP = tActiveEdgePairHead.next; if(_pAEP == NULL) tActiveEdgePairHead.next = pAEP; else{ while(_pAEP->next) _pAEP = _pAEP->next; _pAEP->next = pAEP; } } //画出多边形的上边界线 GLdouble zx = pAEP->zl; int x = pAEP->xl ; //首先寻找上边界的左边界 while(x < 0){ zx += pAEP->dzx; x ++; } while(x < pAEP->xr){//此处不考虑边界 if(x < g_zbuffer.size() && zx > g_zbuffer[x]){ g_zbuffer[x] = zx; //cout<<"x:"<<x<<", y:"<<y<<endl; frameBuffer.SetPixel(x, y, g_bgColor); } zx += pAEP->dzx; x ++; } //}//while(pCE) //清除从分类多边形表中拷贝来的脏指针next //nodeClassifiedPolygon* next_pCP = pCP->next; //pCP->next = NULL; pCP = t_pCP; }//while(pCP) }//if(tClassifiedPolygon[i]) }首先检查下表,这一点相当重要!
!!!在每一次使用[]形式的下标之前一定要检查。。检查。!
检查!!
说三遍。首先看当前扫描线是否有须要增加活化表的多边形,假设有。则增加。而且将其非水平的两条边组成边对增加活化边表,因为是刚增加,那么当前扫描线一定是当前多边形的上边界线,所以毫不犹豫画出边界线。该方法用到的findEdge:
void findEdge(nodeClassifiedEdge** e, GLint y, GLint polygon_id){ //依据polygon_id寻找仍在活化多边形表中的活化边 //e - 结果边的指针的地址 //y - 当前扫描线位置 //polygon_id - 多边形id *e = NULL; nodeActivePolygon* _pAP = (tActivePolygonHead.next); //检查多边形是否还在活化多边形表中 while(_pAP && (_pAP->id != polygon_id || _pAP->dy <= 1)) //_pAP->dy <= 1 这种多边形即将被移出活化表。所以不予考虑 _pAP = _pAP->next; if(_pAP != NULL){//多边形还在活化表中 if(!tClassifiedEdge[y]) { //输出下列语句也有可能是正常现象。由于有可能扫描到多边形最底部时,多边形还没有移出 //活化多边形表 #ifdef DEBUGGER cout<<"function:findedge accur logic error(1)"<<endl; #endif return; } nodeClassifiedEdge* _pCE = tClassifiedEdge[y]; while(_pCE && (_pCE->id != polygon_id || _pCE->used)) _pCE = _pCE->next; if(!_pCE){ #ifdef DEBUGGER cout<<"function:findedge not find"<<endl; #endif return; } _pCE->used = true; *e = _pCE; } }8.深度式增量更新:
void deepthUpdate(GLint y){ //增量式深度更新 nodeActiveEdgePair* pAEP = tActiveEdgePairHead.next; while(pAEP){ GLdouble zx = pAEP->zl; int x = pAEP->xl ; //init_g_zbuffer(); //左右边界着背景色 //首先寻找左边界 while(x < 0){ zx += pAEP->dzx; x ++; } //左边界着色 if(x < g_zbuffer.size() && zx > g_zbuffer[x]){ g_zbuffer[x] = zx; frameBuffer.SetPixel(x, y, g_bgColor); } zx += pAEP->dzx; x ++; //左边界着色完毕 while(x < g_zbuffer.size() && x </*=*/ pAEP->xr){//此处不考虑边界 if(zx > g_zbuffer[x]){ g_zbuffer[x] = zx; //cout<<"x:"<<x<<", y:"<<y<<endl; frameBuffer.SetPixel(x, y, pAEP->color); } zx += pAEP->dzx; x ++; } //右边界着色 //if(zx > g_zbuffer[x]){ // g_zbuffer[x] = zx; // frameBuffer.SetPixel(x, y, g_bgColor); //} //右边界着色完毕 pAEP = pAEP->next; } }这种方法比較简单,检查当前扫描线是否有边对。假设有,则依据当前边对之间的扫描线的像素深度和深度缓存相应位置的深度比較,依据比較结果对帧缓存着色。
void activeEdgeTableUpdate(GLint y){//活化边表元素改动 nodeActiveEdgePair* pAEP_pre = &tActiveEdgePairHead; nodeActiveEdgePair* pAEP = pAEP_pre->next; while(pAEP){ pAEP->dyl --; pAEP->dyr --; if(pAEP->dyl <= 0 && pAEP->dyr <= 0){//左右两边同一时候扫描完 nodeClassifiedEdge *l, *r; findEdge(&l, y, pAEP->id); if(l == NULL){ pAEP_pre->next = pAEP->next; free(pAEP); pAEP = pAEP_pre->next; }else{//假设找到了左边,依据多边形的封闭性。一定能够找到右边 findEdge(&r, y, pAEP->id); if(l->x > r->x || (l->x == r->x && l->dx > r->dx)){ nodeClassifiedEdge* _t = l; l = r; r = _t; } nodeActivePolygon *pAP; findPolygon(&pAP, pAEP->id); pAEP->xl = l->x; pAEP->dxl = l->dx; pAEP->dyl = l->dy; pAEP->xr = r->x; pAEP->dxr = r->dx; pAEP->dyr = r->dy; pAEP->zl = (- pAP->d - l->x * pAP->a - y * pAP->b) / pAP->c; pAEP->dzx = (- pAP->a) / pAP->c; pAEP->dzy = pAP->b / pAP->c; pAEP_pre = pAEP; pAEP = pAEP->next; } } else{ if(pAEP->dyl <= 0){//左边扫描完 nodeClassifiedEdge* l; findEdge(&l, y, pAEP->id); if(l){//pAEP不为空表示多边形仍在活化多边形行表中 //nodeActivePolygon *pAP; //findPolygon(&pAP, pAEP->id); pAEP->xl = l->x; pAEP->dxl = l->dx; pAEP->dyl = l->dy; //pAEP->zl = (- pAP->d - l->x * pAP->a - i * pAP->b) / pAP->c; //pAEP->dzx = (- pAP->a) / pAP->c; //pAEP->dzy = pAP->b / pAP->c; } else{ pAEP_pre->next = pAEP->next; free(pAEP); pAEP = pAEP_pre->next; continue; } } else{//左边未扫描完 pAEP->xl += pAEP->dxl; pAEP->zl = pAEP->zl + pAEP->dxl * pAEP->dzx + pAEP->dzy; } if(pAEP->dyr <= 0){//右边扫描完 nodeClassifiedEdge* r; findEdge(&r, y, pAEP->id); if(r){ pAEP->xr = r->x; pAEP->dxr = r->dx; pAEP->dyr = r->dy; } else{ pAEP_pre->next = pAEP->next; free(pAEP); pAEP = pAEP_pre->next; continue; } } else{//右边未扫描完 pAEP->xr += pAEP->dxr; } pAEP_pre = pAEP; pAEP = pAEP->next; } } }这个更新有几个特别的地方。首先须要区分同一时候扫描完和单边扫描完,同一时候扫描完则须要新的边对。当仅仅有单边扫描完时,不用更新深度信息。由于在增量式深度更新其中已经完毕了深度的更新,最根本的原因还是由于多边形的连贯性。
10.更新活化多边形表
void activePolygonTableUpdate(){//更新活化多边形表 nodeActivePolygon* pAP = tActivePolygonHead.next; nodeActivePolygon* pAP_pre = &tActivePolygonHead; while(pAP){ pAP->dy = pAP->dy - 1; if(pAP->dy <= 0){ pAP_pre->next = pAP->next; free(pAP); pAP = pAP_pre->next; } else{ pAP_pre = pAP; pAP = pAP_pre->next; } } }非常easy。就是检查跨越的扫描线数目。
11.清除数据结构:
void clearData(){ clearActivePolygonTable();//清空活化多边形表 clearActiveEdgeTable();//清空活化边表 clearClassifiedPolygon(); clearClassifiedEdge(); } void clearActivePolygonTable(){//清空活化多边形表 nodeActivePolygon *p = tActivePolygonHead.next, *q; while(p){ q = p->next; free(p); p = q; } tActivePolygonHead.next = NULL; } void clearActiveEdgeTable(){//清空活化边表 nodeActiveEdgePair *p = tActiveEdgePairHead.next, *q; while(p){ q = p->next; free(p); p = q; } tActiveEdgePairHead.next = NULL; } void clearClassifiedPolygon(){ nodeClassifiedPolygon *p, *q; /*int _cnt = 0;*/ for(GLuint i = 0; i < tClassifiedPolygon.size(); i++){ if(tClassifiedPolygon[i]){ p = tClassifiedPolygon[i]; while(p){ q = p->next; free(p); p = q; //_cnt++; } //free(p); tClassifiedPolygon[i] = NULL; } } } void clearClassifiedEdge(){ nodeClassifiedEdge *p, *q; for(GLuint i = 0; i < tClassifiedEdge.size(); i++){ if(tClassifiedEdge[i]){ p = tClassifiedEdge[i]; while(p){ q = p->next; free(p); p = q; } tClassifiedEdge[i] = NULL; } } }
这个模型有34843个顶点,69451个面片。帧率是0.67.
以下是源码【都看到这儿了,给个star呗,感谢感谢】
https://github.com/ooooooops/scan-line-Z-buffer
我的第一感觉是全然不是必需,事实上这个东西本质上就是一张...
1条评论