正文

一个简单的软件渲染引擎2009-01-08 12:17:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/insky/40227.html

分享到:

软件渲染引擎实现说明作者: insky(李文耀) 主页:gamecoding.cn 下载   本程序实现了一整套3D渲染流水线,首先抽象出一个画点函数DrawPoint( int x, int y, DWORD color ),后继模型的所有绘制操作都是通过调用DrawPoint来完成的。实现功能及流程可由下图表示: 1  软件效果及速度        算法实现的最终效果如上图所示,在扫描线Z缓冲算法基础上增加了T&L,背面剔除,屏幕空间裁剪,颜色插值,全屏反走样等一整套3D渲染流水线。最终效果跟D3D9固定流水线的渲染效果已经相当接近,除了一些少数的黑点无法剔除,主要原因是顶点浮点坐标跟屏幕整型坐标的转换带来的锯齿。另外在速度上,对于有92856个面片的兔子模型,软件渲染依然有6.16 fps. 测试电脑配置如下:       CPU             显卡      内存Intel Core 2 E7300(双核,2.66GHz)  NVIDIA GeForce 9600 GT  2G2 编程环境,操作说明         ①编程环境为    vs 2005 + Directx 9.0(c) + DXUT(跟glut类似,DX框架)。   ②用户界面使用说明    中间是当前查看的OBJ文件模型,可以通过鼠标左键旋转模型,也可通过右键水平移动模型以及通过滚轮前后  移动模型。     右边是功能按钮,单击 打开文件 按钮会跳出文件选择对话框,用于加载新的OBJ模型。也可通过 下拉框 直  接选择程序默认的4个OBJ模型。    选择按钮允许用户选择使用D3D9渲染还是使用软件渲染。下面是一堆渲染选项开关:自动旋转,使用光照模   型,全屏反走样,背面剔除,深度测试。 3 软件渲染过程中使用的数据结构与算法说明  所有的操作都封装在类ZLBuffer中,代码1000行左右,其接口只有四个如下:   ZLBuffer();                                                                // 构造函数      HRESULT LoadFromOBJFile( const WCHAR* strFileName );                       //加载OBJ模型       void RenderByD3D9( IDirect3DDevice9* pd3dDevice, float fElapsedTime );     // D3D9渲染   void RenderBySoftWare( IDirect3DDevice9* pd3dDevice, float fElapsedTime );// 软件渲染       bool MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );       // windows消息处理   3.1        加载OBJ模型,填充原始顶点数据结构和面数据结构   HRESULT ZLBuffer::LoadFromOBJFile( const WCHAR* strFileName )函数负责加载模型,填充下面两个数据结构:   std::vector<PrimaryVertex>  m_PrimaryVertexSet;  // 模型原始顶点集合   std::vector<Face>        m_FaceSet;           // 模型原始面集合   // 原始顶点结构   struct PrimaryVertex   {    D3DXVECTOR3 position;     D3DXVECTOR3 normal;     int t;                     };   // 原始面结构   struct Face   {     int p0,p1,p2;   };   其中,D3DXVECTOR3定义如下,t成员是用来记录该顶点属于多少个面,如此计算法法线向量的时候可以直接把每个面的法线向量加到normal再除t来完成。   struct D3DXVECTOR3   {         float x;        float y;        float z;   };   3.2 软件渲染RenderBySoftWare代码仅三行,如下   TANDL();             // 坐标变换和光照过程   DoZLBuffer();        // 光栅化过程结果写到m_ColorBuffer二维数组   Flush( pd3dDevice );  // 把m_ColorBuffer数组写到后台颜色缓存,先进行// 全屏反走样处理   DoZLBuffer就是扫描线Z缓冲算法,其中加入了背面剔除跟裁剪,很大程度上提高了渲染速度。   3.3写后台缓存代码如下:   // 把m_ColorBuffer二维颜色数组里的数据刷到后台颜色缓存   void ZLBuffer::Flush( IDirect3DDevice9* pd3dDevice )   {        HRESULT hr = S_OK;        D3DLOCKED_RECT rect;        IDirect3DSurface9 *pSurface = NULL;        //获取后台表面        V( pd3dDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &pSurface ) );            V( pSurface->LockRect( &rect, 0, D3DLOCK_DISCARD ) );        DWORD *pixels = (DWORD *)(rect.pBits);    // 后台数据指针        rect.Pitch >>= 2;                         // 每一行的跨度,字节数        int num = 0;        for (int y = 0; y < HEIGHT; y++)        {            for (int x = 0; x < WIDTH; x++)            {                 pixels[num + x] = m_ColorBuffer[y][x];            }            num += rect.Pitch;        }        pSurface->UnlockRect();        V( pSurface->Release() );   // 释放表面指针   } 附:ZLBuffer.h文件源代码(完整工程 和 可执行程序请下载附件): //------------------------------------------------------------------------- // File: ZLBuffer.h // ZLBuffer类及相关结构体的声明 // 作者: 李文耀(gamecoding.cn) // 时间:2009-01-02 // // 该类实现了一个简单的3D流水线,自己抽象出了一个画点函数DrawPoint( int x, int y, DWORD color ), // 模型的所有绘制操作都是调用DrawPoint来完成的。模拟了D流水线中的“T&L”并用扫描线Z缓冲算法 // 实现rasterization,同时实现OBJ模型文件的读取。 // 为了优化渲染速度,实现了背面剔除跟裁剪; // 为了改善画面,实现了,线性插值,全屏幕反走样。 // // 使用 // 1,加载模型:调用LoadFromOBJFile( const WCHAR* strFileName )加载模型。 // 2,渲染模型:为了便于比较,提供了两个渲染函数RenderByD3D9( IDirect3DDevice9* pd3dDevice ), //    RenderBySoftWare。前者使用D3D9固定流水线渲染,后者为纯软件渲染。 // 3,操作:可以使用鼠标旋转,拖拉模型,可开启或关闭开关:光照,背面剔除,反走样,深度测试 // // 软件渲染RenderBySoftWare中使用到D3D9的内容罗列如下: // 1, 写后台缓存,这是唯一与D3D9流水线交互的地方。 // 2,矩阵类D3DXMATRIX以及矩阵操作相关函数如:生成视野矩阵的D3DXMatrixLookAtLH // 3,向量类D3DXVECTOR3以及向量操作相关函数如:计算叉积D3DXVec3Dot // // 软件渲染RenderBySoftWare代码仅行,如下: //       TANDL();             // 坐标变换和光照过程 //       DoZLBuffer();        // 光栅化过程结果写到m_ColorBuffer数组 //       Flush( pd3dDevice );  // 把m_ColorBuffer数组写到后台颜色缓存 //-------------------------------------------------------------------------------------- #ifndef __ZLBufferH__ #define __ZLBufferH__ #pragma warning(disable: 4995) #include <vector> #include <list> #pragma warning(default: 4995) #define WIDTH  800 #define HEIGHT 600 #define MINZ   0 #define MAXZ   65535 #define BACKGROUND BLUE // 颜色常量 const D3DXCOLOR      WHITE( D3DCOLOR_XRGB( 255, 255, 255 ) ); const D3DXCOLOR      BLACK( D3DCOLOR_XRGB( 0, 0, 0 ) ); const D3DXCOLOR        RED( D3DCOLOR_XRGB( 255, 0, 0 ) ); const D3DXCOLOR      GREEN( D3DCOLOR_XRGB(  0, 255, 0 ) ); const D3DXCOLOR       BLUE( D3DCOLOR_XRGB(  0, 0, 255 ) ); const D3DXCOLOR     YELLOW( D3DCOLOR_XRGB( 255, 255, 0 ) ); const D3DXCOLOR       CYAN( D3DCOLOR_XRGB( 0, 255, 255 ) ); const D3DXCOLOR    MAGENTA( D3DCOLOR_XRGB( 255, 0, 255 ) ); // 原始顶点结构 struct PrimaryVertex {      D3DXVECTOR3 position;      D3DXVECTOR3 normal;      int t;                               }; // 原始面结构 struct Face {      int p0,p1,p2; }; // 经过“T&L”流水线后的顶点结构 struct TLVertex {      D3DXVECTOR3 position;      float         color; }; // 边表成员结构体 struct Edge {      int id;            // 所属多边形ID      int minY;     // 最小Y      int x,z;      // 边的minY端点的x,z坐标(minY对应的x坐标)      int dy;            // 边跨越的扫描线数目      float dx,dz;  // 两相邻扫描线交点的x坐标之差,z坐标之差           float color;  // 边的minY端点颜色值      float dc;     // 两相邻扫描线交点的颜色值之差 }; // 多边形表成员结构体 struct Triangle {      int id;              // 多边形ID      char state;          // 当前状态,1,2      float a,b,c,d;    // 多边形平面参数      float dzX,dzY;       // Z值沿X轴,Y轴每像素改变值      DWORD color;    // 颜色 }; // 活化边表成员结构体 struct ActiveEdge {      int id;       // 多边形ID      float xL;     // 左交点的x坐标      int   dyL;    // 和左交点所在边相交的剩余扫描线数      float dxL;    // 左边两相邻扫描线交点的x坐标之差      float xR;     // 右交点的x坐标      int   dyR;    // 和右交点所在边相交的剩余扫描线数      float dxR;    // 右边两相邻扫描线交点的x坐标之差      float zL;     // 左交点深度值      float dzX,dzY;// 沿扫描线向右每像素深度增量,沿y方向每像素深度增量           float cL,dcL; // 左交点颜色值,左边两相邻扫描线交点的颜色值之差      float cR,dcR; // 右交点颜色值,右边两相邻扫描线交点的颜色值之差 }; class ZLBuffer { public:      ZLBuffer(); // 加载指定的OBJ文件,会先调用Clear清空原有数据                                          HRESULT LoadFromOBJFile( const WCHAR* strFileName ); // 用D3D9绘制当前模型      void RenderByD3D9( IDirect3DDevice9* pd3dDevice, float fElapsedTime );       // 用软件绘制当前模型      void RenderBySoftWare( IDirect3DDevice9* pd3dDevice, float fElapsedTime ); // 键盘,鼠标消息处理函数      bool MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );         private:      std::vector<PrimaryVertex>  m_PrimaryVertexSet;           // 模型原始顶点集合      std::vector<Face>           m_FaceSet;                    // 模型原始面集合      std::vector<TLVertex>       m_TLVertexSet;                // 经T&L流水线后的顶点集合      std::vector<Triangle>       m_TriangeSet;                 // 多边形集合      std::list<Edge>             m_EdgeSet[HEIGHT];            // 边集合      std::list<ActiveEdge>       m_ActiveEdgeSet;              // 活跃边集合      D3DXVECTOR3                      m_position;                        // 模型的世界坐标      float                            m_rotateY;                         // 模型绕Y轴旋转角度      float                            m_rotateX;                         // 模型绕X轴旋转角度 // 与后台颜色缓存等大的颜色数组,先渲染到此数组然后再一次性写到后台颜色缓存      DWORD                            m_ColorBuffer[HEIGHT][WIDTH];                            DWORD                            m_TempColorBuffer[HEIGHT][WIDTH];  // 临时颜色数组      bool                        m_bIsLightAble;               // 是否使用光照      bool                        m_bIsAntiAliasing;            // 是否抗锯齿      bool                        m_bIsBackCull;                // 是否背面剔除      bool                        m_bIsDepthTest;               // 是否开启深度测试      bool                        m_bIsAutoRotate;              // 是否自动旋转      WCHAR                       m_fileName[100];              // 当前模型文件名 // 软件渲染相关函数 private:      void DrawPoint( int x, int y, DWORD color );   // 打点(屏幕空间),写到m_ColorBuffer数组      void Flush( IDirect3DDevice9* pd3dDevice );    // 将m_ColorBuffer数组刷到后台缓存      void TANDL();                                  // T&L过程,实现了平行光光照模型      void DoZLBuffer();                             // 光栅化过程即扫描线Z缓冲算法 public:      inline void Clear();                         // 清空所有模型相关数据,并初始化      void SetLightAble( bool isLightAble )        { m_bIsLightAble = isLightAble; }      void SetAntiAliasingAble( bool isAnti )     { m_bIsAntiAliasing = isAnti; }      void SetBackCullAble( bool isBackCull )     { m_bIsBackCull = isBackCull; }      void SetDepthTestAble( bool isDepthTest )   { m_bIsDepthTest = isDepthTest; }      void SetAutoRotateAble( bool isAutoRotate ) { m_bIsAutoRotate = isAutoRotate; }      int  GetVertexCount()                      { return m_TLVertexSet.size(); }      int  GetFaceCount()                         { return m_FaceSet.size(); }      const WCHAR * GetRenderFileName()           { return m_fileName; } }; #endif // __ZLBufferH__

阅读(3816) | 评论(1)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册