• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            D3D常用API

            D3DAPI大全,全部函數

            //Direct3D 9.0 SDK 開發參考Direct3D 9.0 SDK 文檔 (中文版)

            詞匯表

            DirectX 8 教程

            你也可以把 COM 對象就想象成一套為某個主題而設計的一整套庫函數。DX 就提供了一套完整的設計3D游戲的庫。

            http://baike.baidu.com/view/1169027.htm

            使用DirectX的不同組件,你需要鏈接不同的靜態庫。例如你要使用DirectDraw組件,你就需要ddraw.lib。

            對于DirectDraw,這個頭文件是ddraw.h。//com編程

            D3D.H

            http://wowe1314.blog.163.com/blog/static/2358876200751191213583/

            在Direct3D編程中,我們要做的工作基本上可以歸納為:

            調用適當的函數獲取接口指針;

            調用接口的方法(成員函數)來完成所需功能;

            用完接口后,調用Release方法進行“釋放”,注意釋放順序應該和獲取它們的順序相反。

            http://www.lihuasoft.net/article/show.php?id=2928

            Microsoft_DirectX_9.0c里的 9個DirectX的DLL

            DX9和DX10在渲染流水線上都是有天壤之別的,好在DX高版本開發包運行庫中包含了對低版本開發包運行庫的實現,所以用DX8開發的程序,DX9運行庫也能夠很好的支持,在安裝有D9運行庫的系統上跑DX8開發的程序不需要再安裝DX8運行庫,但是這個兼容性支持在最近被微軟逐漸放棄,有時候DX9的不同更新版本做的程序也不能向下兼容,比如DX9FEB2007SDK,同DX9AUG2006SDK在shader編譯規則上也是不同的,2007放棄了VS2.0和PS2.0以下版本shader的支持,同時對于HLSL中#include相對路徑引用的默認根目錄也是有區別的.openGL的shader擴展不同的廠商有不同的擴展開發包,但是這種情況隨著GLSL和openGL2.0的出現有所改觀.同時OpenGL是跨平臺的而DX不是,這意味著用OpenGL和GNU   C++規則開發的程序可以同時在Linux,unix和安裝有GNU環境的Windows上同時運行。從效率上來看,DX由于數據時批量寫入顯存的,同OpenGL的單條函數寫入來講DX效率上要高一些,不過近來OpenGL也支持了批寫入,只是支持批寫入的OpenGL放棄了openGL一慣的優勢也就是語言架構上的簡潔使得函數的數目變得很冗雜。在效果上看DX9同支持GLSL或CG擴展的openGL可以實現相同的顯示效果。但是有一點不同是DXUT和D3DX在一些基礎繪制上比glu和openGL   ARB   Extend要差一點,比如繪制虛線,DX沒有好的函數可以是實現這一功能。但是DX的擴展工具比openGL擴展工具又有多余的優勢比如向量計算,GUI控件,mesh優化和曲面展開,PRT預計算等等和性能測試等等上又要強一點。DX10同OpenGL比較就感覺openGL不是同一個數量級上的產品,DX10在渲染流水線和架構上和能夠實現的效果上要比DX9和openGL進步的多。要做面向未來的游戲產品盡量還是用DX10吧。

            ­

            LPDIRECT3D9 D3D主接口

            LPDIRECT3DDEVICE9 D3D硬件主接口

            LPDZRECT3DVERTXBUFFER9 頂點緩沖區接口

            LPD3DVIEWPORT9  視口接口

            LPD3DDISPLAYMODE D3D設備顯示模式接口

            LPD3DPRESENT_PARAMETERS 創建結構用來保存設備的顯示模式接口

            LPD3DXVECTOR3  3D向量接口

            LPDIRECT3DTEXTURE9 紋理接口

            ID3DXSprite  精靈接口

            g.pvb  成員函數

            g_pD3D  成員函數

            g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddm) 獲取顯示模式

            g_pd3dDevice 成員函數

            g_pd3dDevice->SetRenderState(,BOOL) 是否開啟燈光

            g_pd3dDevice->SetTransform( D3DTS_WORLD, &(matWorld * matWorld_x));//將上面計算出來的旋轉矩陣,設置為世界變換矩陣

            g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );寫入流

            g_pd3dDevice->SetFVF() 設置FVF

            g_pd3dDevice->DrawIndexedPrimitive( 畫形狀, 0, 0, 點個數, 0, 三角形個數 ); 畫

            timeGetTime 當前系統的時間

            DIRECT3DTXTURE 文理接口

            BITMAPPEILEHEADER 文件頭

            BITMAPINFOHEADER 信息頭

            fread 讀一個數據塊

            biBitcout 每個象素占幾個字節

            bicompression 是否被壓縮

            fseek 跳,偏移指針

            greatetxture 創建一個空文理

            D3Dcaked_RECT 鎖定結構體

            setTexturestagestata 設置文理操作

            CONSTD3DMATRIX*  進行變換的變換矩陣

            結構體

            D3DPRESENT_PARAMETERS 存儲D3D設備信息

            D3DXMATRIX  修改矩陣

            數組

            CUSTOMVERTEX 設置頂點位置顏色信息

            ­

            矩陣函數

            D3DXMATRIX * D3DXMatrixIdentity(POut,pM) 單位矩陣

            D3DXMATRIX * D3DXMatrixTranspose(上)  矩陣轉置

            D3DXMATRIX * D3DXMatrixInverse(上中間加個FLOAT) 逆矩陣

            D3DXMATRIX * D3DXMatrixTransformation()

            D3DXMATRIX* D3DXMatrixTranslation(輸出矩陣,X,Y,Z) 平移變換

            D3DXMATRIX * D3DXMatrixScaling(上) 縮放變換

            FLOAT D3DXPlaneDotCoord(pp,pv) 點和平面之見的關系

            D3DXPLANE * D3DXPlaneFromPointNormal(POUT,PPOINT,PNORMAL) 構造子

            D3DXPLANE * D3DXPlaneFromPoints(Pout,pv1,pv2,pv3) 通過點來描述平面

            D3DXPLANE * D3DPlaneNormalize(POUT,PP) 標準化一個平面

            D3DXPLANE * D3DXPlaneTransform(POUT,PP,PM) 平移平面

            D3DXM

            轉換函數

            D3DXMATRIX* D3DXMatrixLookAtLH(輸出用于視圖轉換的矩陣,攝象機的位置,攝象機面向的位置,攝象機的正方向)  視圖轉換的矩陣

            D3DXMATRIX* D3DXMatrixOrthoLH(輸出用于正交投影的交換矩陣,取景寬,取景高,取景離攝象機的最近距離,取景離攝象機的最遠距離) 正交投影變換矩陣

            D3DXMATRIX* D3DXMatrixPerspectiveFovLH(輸出用于透視投影的交換矩陣,攝象機鏡頭的夾角Y,平截臺體的縱橫比,近平截面的距離,遠平截面的距離) 透視投影的矩陣

            Direct3DCreate9(D3D版本) 創建D3D對象

            設備函數

            SetTransform(變換的類型,變換的變換矩陣) 設置左手或右手坐標

            SetViewport(視口指針) 設置遠近距離

            GetClientRect(hWnd,*RECT) 獲取窗口繪圖區域

            memcpy(指針,數組,長度) 拷貝

            SetStreamSource(0,G.pvb接口指針,0,長度) 數據流

            GetAdapterDisplayMode(指定顯示卡序列號,存儲顯示模式的指針) 獲取顯卡的模式

            HRESULT CreateDevice(顯卡序列號,D3D設備類型,所屬窗口句柄,D3D進行3D運算,存儲D3D設備相關信息指針,返回D3D設備借口指針的地址) 創建設備借口

            HRESULT CreateVertexBuffer(頂點緩沖區大小(字節),頂點緩沖區屬性,靈活頂點格式,頂點緩沖區內存位置,頂點緩沖區指針地址,保留參數通常為0) 創建頂點緩沖

            HRESULT CreateIndexBuffer(索引緩沖區大小(字節),頂點緩沖區屬性,FMT顏色,頂點緩沖區內存位置,索引緩沖區指針地址,保留參數通常為0)   創建索引緩沖

            HRESULT Lock(加鎖內存起始地址,加鎖內存大小,返回內存指針地址,加鎖屬性) 加縮內存

            HRESULT UnLock() 解鎖

            HRESULT SetStreamSource(渲染數據流序列號,進行綁定連接的頂點緩沖區指針,進行綁定連接渲染數據流的起始位置,渲染數據流中一個頂點所占的內存大小) 頂點緩沖區和渲染數據流連接

            HRESULT SetFVF(靈活頂點格式) 設置頂點格式

            HRESULT DrawPrimitive(繪制的圖元類型,繪制的開始頂點的索引值,繪制的圖元數量)  畫到后向緩沖區

            HRESULT DrawPrimitiveup() 可以直接畫

            HRESULT Preesent(復制源的矩形區域指針,復制目的地的矩形區域指針,D3D設備窗口句柄,最小更新區域指針) 屏幕翻轉

            HRESULT SetIndices(使用的索引緩沖區指針) 設置當前繪制的索引數組

            DrawIndexedPrimitive(圖元類型,繪制到的索引緩沖區的開始地址,最小的索引數組元素的值,頂點的數目,開始的索引數組元素的值,繪制的數量) 同DrawPrimitive()

            ­

            繪制函數

            HRESULT DrawPrimitive(基本圖元類型,起始頂點,繪制的圖元的數量)  圖元繪制

            HRESULT Clear(清楚的矩形區域數量,清除的舉行區域數組指針,清楚哪個緩沖區,清除后重置的顏色,清除后重置的深度,0-1.0,重置的摸版值) 清空圖形繪制區

            HRESULT BeginScene() 開始繪制

            HRESULT EndScene() 結束繪制

            ­

            紋理函數

            CreateTexture()  創建D3D紋理對象

            LoadBmpTeture() 裝載文理函數

            LoadBmpTexture24Bit (LPDIRECT3DDEVICE9 pDevice,LPCSTR  pSrcFile,LPDIRECT3DTEXTURE9* ppTexture) 24位紋理

            D3DXCreateTextureFromFile(D3D設備指針,紋理圖形文件,存儲D3D文理的指針地址) 直接從磁盤獲取紋理

            D3DXCreateTextureFromFileEx(D3D設備指針,紋理圖形文件,指定紋理寬,高,指定漸進紋理序列級數,紋理使用方式一般為0,指定紋理圖形格式,紋理存放的內存類型一般位為0,紋理過濾方式,自動生成的紋理序列過濾方式,設置透明色,圖形文件信息存放地址可設置0,調色板存儲地址,創建的D3D文理的指針地址) 高級獲取紋理

            HRESULT SetTexture(多級紋理的索引0-7,D3D的紋理接口指針) 設置當前要渲染的紋理

            HRESULT SetTextureStageState(多級紋理的索引,紋理渲染狀態的類型,紋理渲染狀態的值,與類型相對應) 設置紋理的渲染狀態

            HRESULT SetSamplerState(指定紋理屬性0-7,紋理采樣屬性類型,設置紋理采樣屬性) 紋理采樣

            HRESULT CheckDeviceFormat(指定顯卡序列號,D3D設備類型,指定顯示模式格式,緩沖區屬性,需要使用查詢的格式的設備類型,需要查詢的顯示格式) 紋理壓縮

            HRESULT LockRect(指定加鎖的紋理級別,指向D3DLOCKED_RECT結構,要加鎖的RECT區域-0代表整個區域,加鎖類型-取0或下表的值) 鎖定紋理

            HRESULT UnlockRect(解鎖的紋理級別) 解鎖紋理

            向量函數

            D3DXVECTOR3 * D3DXVer3Length(V) 向量模的計算

            D3DXVECTOR3 * D3DXVec3Normalize(返回指針,V) 單位化

            D3DXVECTOR3 * D3DXVec3Add(返回的指針,u,v) 向量加法

            D3DXVECTOR3 * D3DXVec3Subtract(同上) 減法

            D3DXVECTOR3 * D3DXVec3Cross(同上) 向量X乘

            D3DXVECTOR3 * D3DXVec3Lerp(同上) 數乘

            D3DXVECTOR3 * D3DXVec3Maximize(同上) 取最大值

            D3DXVECTOR3 * D3DXVec3Minimize(同上) 取最小值

            D3DXVECTOR3 * D3DXVec3Scale(返回指針,PV,FLOAT) 比例

            FLOAT D3DXVec3Dot(pv1,pv2) 點乘

            參見編程精粹.chm中的COM中模塊的導出函數

            Private Type D3DVECTOR

                x As Single

                y As Single

                z As Single

            End Type

            '返回3D向量的規格化向量

            Private Declare Function D3DXVec3Normalize Lib "DX8VB.DLL" Alias "VB_D3DXVec3Normalize" (VOut As D3DVECTOR, v As D3DVECTOR) As Long

            Private Declare Function D3DXVec3Add Lib "DX8VB.DLL" Alias "VB_D3DXVec3Add" (VOut As D3DVECTOR, v1 As D3DVECTOR, V2 As D3DVECTOR) As Long

            Private Declare Function D3DXVec3Subtract Lib "DX8VB.DLL" Alias "VB_D3DXVec3Subtract" (VOut As D3DVECTOR, v1 As D3DVECTOR, V2 As D3DVECTOR) As Long

            Private Declare Function D3DXVec3Length Lib "DX8VB.DLL" Alias "VB_D3DXVec3Length" (v As D3DVECTOR) As Single

            D3DFVF 自由頂點的格式

            D3DFVF_DIFFUSE 包含謾反射的信息

            D3DFVF_NORMAL 包含法線信息

            D3DFVF_PSIZE 頂點信息指明繪制點的大小

            D3DFVF_SPECULAR 包含鏡面反射的信息

            D3DFVF_XYZ 包含未經轉換的頂點坐標

            D3DFVF_XYZRHW 包含經過轉換的頂點坐標

            D3DFVF_XYZB1 through D3DFVF_XYZB5 包含用于骨骼動化的頂點和頂點對骨骼的權重信息

            D3DFVF_XYZW 包含經過轉換和裁剪的頂點坐標

            D3DTRANSFORMSTATETYPE 變換的類型

            ­

            D3DPRIMITIVETYPE 定義基本圖元

            D3DPT_POINTLIST 一組點的集合

            D3DPT_LINELIST 一組線的集合

            D3DPT_LINESTRIP 首尾相連的線段的集合

            D3DPT_TRIANGLELIST 一組三角形的集合

            D3DPT_TRIANGLESTRIP 首尾相連的三角形,有兩個頂點集合

            D3DPT_TRIANGLEFAN   組成扇形的一組三角形集合

            D3DPT_FORCE_DWORD 未定義的

            D3DDISPLAYMODE 屏幕顯示模式

            D3DFMT_UNKNOWN  未知的象素格式

            D3DFMT_R8G8B8  24位色,RGB各占8位

            D3DFMT_A8R8G8B8  32位色,@RGB各占8位

            D3DFMT_X8R8G8B8  32位色,X為保留8位 RGB各占8位

            D3DFMT_R5G6B5  16位色,R占5,G占6,B占5位

            D3DFMT_X1R5G5B5  16位色,保留1位,RGB各占5位

            D3DFMT_A1R5G5B5  16位色,@占1位,RG5各占5位

            D3DFMT_A4R4G4B4  16位色,@RGB各占4位

            D3DFMT_R3G3B2  8位色,R3,G3,B2位

            D3DFMT_A8  只有8位@

            D3DFMT_A8R3G3B2  16位色,@8,R3,G3,B2位

            D3DFMT_X4R4G4B4  16位色

            D3DFMT_A2B10G10R10 32位色,@占2位,RGB各10位

            D3DFMT_A8B8G8R8  32位色

            D3DFMT_X8B8G8R8  32位色

            D3DFMT_G16R16  32位色,只有紅和綠

            D3DFMT_A2R10G10B10 32位色

            D3DFMT_A16B16G16R16 64位色

            D3DFMT_A8P8  8位色,8位表示半透明,8位表示顏色

            D3DFMT_P8  8位色,用牙色索引值表示

            D3DFMT_L8  8位色,只表示亮度

            D3DFMT_L16  16位色,只表示亮度

            D3DFMT_A8L8  16位色,8位表示半透明,8位表示亮度

            D3DFMT_A4L4  8位色,4位表示半透明,4位表示亮度

            D3DDEVTYPE_HAL   硬件抽象層,通過顯示硬件來完成圖形渲染工作

            D3DDEVTYPE_NULLREF  

            D3DDEVTYPE_REF   參考光柵器,一般用語測試顯示卡不支持的D3D功能

            D3DDEVTYPE_SW   用語支持第三方的軟件

            D3DDEVTYPE_FORCE_DWORD  擴展的

            D3DCREATE 3D運算的方式

            D3DCREATE_ADAPTERGROUP_DEVICE

            D3DCREATE_DISABLE_DRIVER_MANAGEMENT

            D3DCREATE_DISABLE_DRIVER_MANAGEMENT_EX

            D3DCREATE_FPU_PRESERVE   激活雙精度浮點運算或浮點運算異常檢測,設置該項會降低系統性能

            D3DCREATE_HARDWARE_VERTEXPROCESSING 由D3D硬件進行頂點預算

            D3DCREATE_MIXED_VERTEXPROCESSING 由混合方式進行頂點運算

            D3DCREATE_MULTITHREADED   支持多線程繪制,設置該項會降低系統性能

            D3DCREATE_NOWINDOWCHANGES  

            D3DCREATE_PUREDEVICE   禁用D3D的GET*()函數,禁止D3D使用虛擬設備模擬頂點運算

            D3DCREATE_SCREENSAVER  

            D3DCREATE_SOFTWARE_VERTEXPROCESSING 由D3D軟件進行頂點運算

            D3DSWAPEFFECT 取值列表

            D3DSWAPEFFECT_DISCARD    后臺緩沖區復制到前臺時,清除后臺緩沖區內容

            D3DSWAPEFFECT_FLIP    后臺緩沖區內容復制后,保持不變,有多個后臺緩沖區時使用

            D3DSWAPEFFECT_COPY    后臺緩沖區內容復制后,保持不變,只有1個后臺緩沖區時使用

            D3DSWAPEFFECT_FORCE_DWORD   強迫該直作為32位存儲,通常不用

            D3DPRESENT 屏幕反轉模式列表

            D3DPRESENT_DONOTWAIT  

            D3DPRESENT_INTERVAL_DEFAULT 默認的同ONE

            D3DPRESENT_INTERVAL_ONE  當屏幕刷新一次時前臺后臺進行交換

            D3DPRESENT_INTERVAL_TWO  當屏幕刷新二次時前臺后臺進行交換

            D3DPRESENT_INTERVAL_THREE 當屏幕刷新三次時前臺后臺進行交換

            D3DPRESENT_INTERVAL_FOUR 當屏幕刷新四次時前臺后臺進行交換

            D3DPRESENT_INTERVAL_IMMEDIATE 圖形繪制完成時立即進行交換

            D3DPRESENT_LINEAR_CONTENT

            D3DUSAGE 緩沖區屬性值列表

            D3DUSAGE_AUTOGENMIPMAP  

            D3DUSAGE_DEPTHSTENCIL

            D3DUSAGE_DMAP  

            D3DUSAGE_DONOTCLIP 禁用裁剪,表示頂點緩沖區中的頂點不進行裁剪,當設置該屬性時,渲染狀態D3DRS_CLIPPING必須設為FALSE

            D3DUSAGE_DYNAMIC 使用動態內存分配

            D3DUSAGE_NPATCHES 使用頂點緩沖區繪制N-patches曲線

            D3DUSAGE_POINTS  使用頂點緩沖區繪制點

            D3DUSAGE_RENDERTARGET

            D3DUSAGE_RTPATCHES 使用頂點緩沖區繪制曲線

            D3DUSAGE_SOFTWAREPROCESSING 使用軟件進行頂點運算,否則使用硬件計算

            D3DUSAGE_WRITEONLY 只寫屬性,不能進行讀操作,設置該屬性可以提高系統性能

            D3DPOOL  緩沖區資源內存位置列表

            D3DPOOL_DEFAULT  默認的,頂點緩沖區盡可能存在與顯存中

            D3DPOOL_MANAGED  由D3D自動調度頂點緩沖區內存位置(顯存和內存)

            D3DPOOL_SCRATCH  頂點緩沖區位于計算機的臨時內存中,這種類型的頂點緩沖區不能直接進行渲染,只能進行內存枷鎖,拷貝等操作

            D3DPOOL_SYSTEMMEM 頂點緩沖區位于內存中

            D3DLOCK  緩沖區加鎖

            D3DLOCK_DISCARD  更新整個緩沖區

            D3DLOCK_DONOTWAIT

            D3DLOCK_NO_DIRTY_UPDATE 在加鎖的過程中系統進行其他操作(默認有Dirty標記)

            D3DLOCK_NOOVERWRITE 保證不腹稿緩沖區數據,設置該屬性可以立即返回內存指針,提高系統性能

            D3DLOCK_NOSYSLOCK 在加鎖的過程中系統可能執行其他操作

            D3DLOCK_READONLY 設置緩沖區位只讀屬性

            D3DXVECTOR3 向量算法

            D3DXVECTOR3u(x,y,z);

            D3DXVECTOR3v(x,y,z);

            float 變量=D3DXVec3Dot(u指針,v指針) 點乘

            D3DXMATRIX 矩陣

            D3DXMatrixIdentity 單位矩陣

            D3DXMatrixInverse 逆矩陣

            D3D實現圖形變換

            D3DXMatrixTranslation 平移矩陣

            D3DXMatrixLockAtLH 觀察矩陣

            D3DXMatrixIdentity  將一個矩陣單位化

            D3DXMatrixRotationY 繞Y軸轉

            D3DXMatrixRotationX 繞X軸轉

            D3DXMatrixRotationZ 繞Z軸轉

            D3DXMatrixScaling 縮放變換

            D3DXMatrixMuLationAxis 圍繞任意一個軸旋轉

            D3DXMatrixMultiply 組合變換

            D3DUSAGE 紋理使用

            D3DUSAGE_AUTOGENMIPMAP 自動生成多級漸進紋理序列,該方式在資源處于D3DPOOL_SYSTEMMEM時無效

            D3DUSAGE_DEPTHSTENCIL 深度模版緩沖區,只在資源處于D3DPOOL_default時有效

            D3DUSAGE_DMAP  該紋理是一個置換紋理

            D3DUSAGE_DONOTCLIP

            D3DUSAGE_DYNAMIC

            D3DUSAGE_NPATCHES

            D3DUSAGE_POINTS

            D3DUSAGE_RENDERTARGET 該文理是一個渲染目標緩沖區

            D3DUSAGE_RTPATCHES

            D3DUSAGE_SOFTWAREPROCESSING 應用坐標變換

            D3DUSAGE_WRITEONLY

            D3DTEXTURESTAGESTATETYPE 渲染狀態類型

            D3DTSS_COLOROP   1 文理層的顏色混合方式

            D3DTSS_COLORARG1  2 顏色混合的第一個參數

            D3DTSS_COLORARG2  3 顏色混合的第二個參數

            D3DTSS_ALPHAOP   4 指定紋理層的Alpha透明

            D3DTSS_ALPHAARG1  5 Alpha混合的第一個參數

            D3DTSS_ALPHAARG2  6 Alpha混合的第二個參數

            D3DTSS_BUMPENVMAT00  7 繪制凹凸紋理時

            D3DTSS_BUMPENVMAT01  8 繪制凹凸紋理時

            D3DTSS_BUMPENVMAT10  9 繪制凹凸紋理時

            D3DTSS_BUMPENVMAT11  10 繪制凹凸紋理時

            D3DTSS_TEXCOORDINDEX  11 該紋理層使用的紋理坐標的索引

            D3DTSS_BUMPENVLSCALE  22 繪制凹凸紋理的縮放參數

            D3DTSS_BUMPENVLOFFSET   23 繪制凹凸紋理的平移參數

            D3DTSS_TEXTURETRANSFORMFLAGS  24 控制紋理坐標的轉換標志

            D3DTSS_COLORARG0  26 指定混合過程的第三個顏色

            D3DTSS_ALPHAARG0  27 Alpha混合的第三個參數

            D3DTSS_RESULTARG  28 顏色混合的結果輸出寄存器

            D3DTSS_CONSTANT  32 顏色混合的常量寄存器

            D3DTSS_FORCE_DWORD  0x7fffffff 強制轉換為32位,通常不用

            D3DSAMPLERSTATETYPE 紋理采樣屬性

            D3DSAMP_ADDRESSU  1 包裝紋理

            D3DSAMP_ADDRESSV  2 包裝紋理

            D3DSAMP_ADDRESSW  3 包裝紋理

            D3DSAMP_BORDERCOLOR  4

            D3DSAMP_MAGFILTER  5 處理放大過濾

            D3DSAMP_MINFILTER  6 處理縮小過濾

            D3DSAMP_MIPFILTER  7 多紋理過濾

            D3DSAMP_MIPMAPLODBIAS  8 多級文理級數偏移值,初試直為0

            D3DSAMP_MAXMIPLEVEL  9 最大多紋理級別,初試值為0

            D3DSAMP_MAXANISOTROPY  10 各向異性,初試值為1

            D3DSAMP_SRGBTEXTURE  11

            D3DSAMP_ELEMENTINDEX   12

            D3DSAMP_DMAPOFFSET  13

            D3DSAMP_FORCE_DWORD  0x7fffffff 強制轉換32位,通常不用

            紋理尋址

            D3DTADDRESS_WRAP  1 包裝紋理尋址

            D3DTADDRESS_MIRROR  2 鏡像紋理尋址

            D3DTADDRESS_CLAMP  3 夾取紋理尋址

            D3DTADDRESS_BORDER  4 邊框顏色紋理尋址

            D3DTADDRESS_MIRRORONCE  5 一次鏡像紋理尋址

            D3DTADDRESS_FORCE_DWORD 0x7fffffff強制轉換32位,通常不用

            世界變換

            D3DTS_WORLD  世界變換

            posted @ 2009-09-17 07:55 RedLight 閱讀(3684) | 評論 (0)編輯 收藏

            D3d9的一些更新 (轉)

            由于Aug 8造成的D3D9恐懼癥已經完全消除了,這一章將會給大家介紹將3D引擎轉向D3D9的各個方面,包括終于出現的全屏幕模式。從這章以后,我將使用D3D9作為講解的語言繼續D2D教程。

            【OP結束,開始正片】

            『Why?』

              估計大家首先要問的就是“Why?”為什么要前進到D3D9?理由如下:
            1、D3D9修復了D3D8已知的所有Bug,因此運行起來更穩定,速度也要快。
            2、D3D9提供了許多便利的新功能,雖然絕大多數是面向3D的,但是也有不少2D適用的,比如IDirect3DDevice9::StretchRect,以及對IDirect3DSurface9的改進等等。D3DX庫就更多了,比如D3DXSaveSurfaceToFileInMemory,一開始沒發現這個函數有啥用處,現在基本離不開了。
            3、HLSL。就像上一話我說的那樣,D2D教程以后會有PixelShader的內容。我可不想拿匯編來寫Shader,會死人的(祝賀我吧,終于拋棄匯編Shader了……)。雖然說這不是決定性的理由,因為還有Cg什么的,不過我想編寫顯卡無關的代碼,因此我不去研究Cg(反正和HLSL差不多)以及R2VB之類。
            4、ID3DXFont,往下看你就知道了。

            《D3D的變化》

            『界面名稱變化』

              一句話:8改成9就行。

            『“創建”型方法的一個統一變化』

              許多Create*()方法,比如創建設備、創建紋理、創建頂點緩沖等等,多了一個HANDLE* pSharedHandle參數,無用,NULL之(看來微軟原打算弄個共享句柄之類,不過被D3D10巨大的變化浮云了)

            『創建D3D設備的變化』

              D3DPRESENT_PARAMS的FullScreen_PresentationInterval變成了PresentationInterval,也就是說即使在窗口模式下也可以做到垂直同步來防止撕裂現象(2D的福音啊)。相應的,D3DSWAPEFFECT_COPY_VSYNC消失了,反正這個效果也不咋的,消失了也好。
              要做到垂直同步需要給PresentationInterval賦值D3DPRESENT_INTERVAL_DEFAULT或D3DPRESENT_INTERVAL_ONE。其中D3DPRESENT_INTERVAL_ONE的效果比D3DPRESENT_INTERVAL_DEFAULT好一點,不過相應的也會占用多一點點系統資源……真的只有一點點而已,實在是無所謂的……
              如果不要垂直同步,想要看看實際禎速的話,D3DPRESENT_INTERVAL_IMMEDIATE。
              注意在窗口模式下,你只能使用這三種Present模式,全屏幕模式下就可以使用別的(但是要首先檢測D3DCAPS9以查看顯卡是否支持)。不過我感覺對99%的游戲來說,有這三個就足夠了。
              另外在窗口模式下,BackBufferFormat也可以設置成D3DFMT_UNKNOWN,D3D會自動獲取當前桌面的格式設定成后備緩沖的格式,省去GetDisplayMode。實際上,窗口模式下的后備緩沖已經不需要和桌面格式相同,你可以通過IDirect3D9::CheckDeviceFormatConversion來檢查,如果這個設備支持這兩種顏色格式之間的轉換,就可以給程序的后備緩沖設定上不同的格式。我試過在桌面格式為32Bit(D3DFMT_X8R8G8B8)時將程序的后備緩沖格式設置為D3DFMT_R5G6B5(16Bit),發現了速度提升,也就是說這個設定是有意義的。
              可創建的設備類型多了一種D3DDEVTYPE_NULLREF,在安裝了D3D SDK的機子上等同于D3DDEYTYPE_REF,在其他的機子上,這種設備實際上沒有創建真正意義的D3D設備,只是允許你創建的紋理、表面等資源,但是Render、Present等操作都會無效(實際上這些資源都創建在了D3DPOOL_SCRATCH池里,不管你設定使用的是什么POOL)。也就是說,僅僅在模擬基本的運行而已。你可以用這個設備來編寫一個利用D3DX函數庫進行圖像格式轉換的程序,比如把一大堆不同的格式轉換成易于D3D9使用的DDS格式。因為實際上沒有創建設備,你甚至可以編寫成控制臺的,通過GetConsoleWindow的方法獲得HWND。Mercury 3用的MIF格式的轉換器就是這么做出來的。注意D3DDEVTYPE_NULLREF只能用在IDirect3D::CreateDevice時,其他的方法都不行。

            『創建表面的變化』

              創建表面(Surface)的方法變成了IDirect3DDevice9::CreateOffscreenPlainSurface,參數很簡單不用多說,需要注意的是可以選擇POOL了。

            『設定FVF的變化』

              設定FVF時,原來通過IDirect3DDevice8::SetVertexShader,現在有了一個專門用來設定FVF的方法:IDirect3DDevice9::SetFVF。這是個很好的變化,省得把FVF和Shader弄混(題外話:也就是因為這個變化,讓Shader在設備Reset后得以保存,不錯不錯)

            『獲取后備緩沖』

              D3D9現在允許有多個后備緩沖交換鏈,不過對于2D來說,基本不需要這種東西,IDirect3DDevice9::GetBackBuffer多出來的第一個參數賦值0即可。如果你有興趣,可以去研究一下這個玩意,有時候可以用來做分場。

            SetStreamSource』

              這個方法的功能被擴展了,對比參數就可以知道,多出來的OffsetInBytes允許你選擇一個頂點緩沖的Offset,D3D9將從這個Offset之后開始讀取數據。因此你可以把幾組用來渲染紋理的正方形頂點存儲到一個頂點緩沖里面。

            SetSamplerState』

              這個是D3D9的新方法,把原先SetTextureStageState的一些功能獨立了出來,和2D關系最密切的就是紋理過濾了。原先的D3DTSS_MINFILTER變成了D3DSAMP_MINFILTER,相應的D3DTSS_MAGFILTER也變成D3DSAMP_MAGFILTER,D3DTSS_MAXANISOTROPY變成D3DSAMP_MAXANISOTROPY。另外還有更多的,比如紋理尋址等。你去看一下D3DSAMPLERSTATETYPE枚舉類型的內容就知道它“遷移”了些什么。
              這個變化對于Shader來說很方便。改成Sampler的東西在PixelShader過程也會有效,而沒有更改的東西在PixelShader就不會有效了。D3D8時候把這些全都放在了一起,容易造成混亂。

            SetRenderTarget』

              D3D9現在允許多重RenderTarget存在,不過我們基本上只用一個,RenderTargetIndex設為0,第二個參數仍然是需要設定的表面。與D3D8相同的是,在設定之前仍然需要先通過GetSurfaceLevel獲得表面才行。

            『頂點緩沖的鎖定』

              注意IDirect3DVertexBuffer9::Lock的第三個參數,從原來的BYTE**變成了void**。也就是這樣了……

            『其他的一些變化』

            1、CopyRects變成了UpdateSurface。和UpdateTexture一樣,只能從D3DPOOL_SYSTEMMEM拷貝到D3DPOOL_DEFAULT
            2、增加了一個比較有用的IDirect3DDevice9::ColorFill方法,作用是向D3DPOOL_DEFAULT的某個區域填充顏色,和Clear的功能類似,但是在使用目的上要比Clear明確的多,并且由于不牽扯深度緩沖之類,速度要快一些。
            3、增加了一個IDirect3DDevice9::StretchRect方法,通過這個方法就可以在D3DPOOL_DEFAULT的表面或紋理之間進行帶過濾器的縮放操作,免去利用Render的過程,非常有用。不過這個方法由于使用了硬件處理,限制較多,請大家仔細看SDK文檔的Remarks部分。

            《D3DX的變化》

              D3DX的變化實際上相當的多,但正如我一開始所說,基本都是面向3D的。需要我們注意的有以下幾種:
            1、D3DX***FromFile之類的函數支持的圖像格式增加了,不過所增加的都是很少見的格式。平時基本上還是用BMP、TGA和PNG就足夠。
            2、增加了D3DXSave***ToFileInMemory,將會把文件寫入內存。這個函數的作用似乎不是很容易想到,但是如果你要寫一個集成了轉換、打包功能的工具,這個就很有用了,省去了通過臨時文件操作造成的各種問題。另外如果你熟悉某種圖形文件的格式的話,還可以通過直接訪問這個文件獲得RAW信息。注意,這類函數寫入的是一個ID3DXBuffer,這個東西很簡單,只有兩個特定的方法,一看便懂,不再多言。
            3、增加了一個ID3DXLine,可以方便你在2D上畫線,創建ID3DXLine的方法是D3DXCreateLine。這個東西也不復雜,使用方法有點像ID3DXSprite,稍微研究一下就能弄懂,注意每次Draw的是D3DPT_LINESTRIP。用它比直接用頂點緩沖的好處是可以方便的打開反鋸齒,效果嘛……基本滿意。
            4、增加了一個ID3DXRenderToSurface,“理論上來說”方便了利用RenderTarget的過程……不過我感覺反而弄得復雜了。創建的方法是D3DXCreateRenderToSurface,有心情的朋友自己研究看看吧,我就不講了。

              ID3DXSprite和ID3DXFont在Summer 2004的DX9 SDK(也就是第一版DX9.0c)開始發生了很大變化,下面詳述:

            『ID3DXSprite』

              你會發現ID3DXSprite::DrawTransform不見了,取而代之的是其功能被整合到ID3DXSprite::SetTransform里面,也就是說為了縮放和旋轉,我們不得不和矩陣打交道了。其實也不會太復雜,因為我們只是做一些矩陣運算,學過線性代數的朋友肯定會很熟悉,就算你不怎么熟悉線性代數,也沒關系,D3DX函數庫提供了現成的矩陣運算函數,你只要用就行了。

            D3DXMatrixScaling
            D3DXMatrixRotationZ
            D3DXMatrixTranslation

              按照順序調用這三個函數……或許學過3D的馬上就想到這點了,的確是沒錯啦。注意順序哦:Scaling -> Rotation -> Translation,簡稱SRT(看過全金屬狂潮嗎?看過的話這個單詞很好記吧^_^),弄錯了可是得不到正確結果的。
              你是不是想到把同一個D3DXMATRIX當作參數使用三次?錯啦!你要用矩陣乘法。創建三個D3DXMATRIX,比如mat1、mat2、mat3,分別用這三個函數將其創建為縮放矩陣、旋轉矩陣和平移矩陣,然后在ID3DXSprite::SetTransform時,這樣寫:

            SetTransform(mat1 * mat2 * mat3);

              有夠麻煩的是不?ID3DXSprite方便了做3D的,可害苦了做2D的,所以我已經不直接用這個了(什么叫不直接用?往下看)。

            『ID3DXFont』

              大家來歡呼吧!Summer 2004改進的ID3DXFont徹底槍斃掉了上一話那個字體引擎……
              這東西的改進,怎么說呢,應該說是改頭換面吧,速度、效果都和以前不是一個數量級。可憐的PixelFont,才存在了一話就要被拋棄了。
              ID3DXFont多出來的幾個方法,Preload*()這類的,就是把一些常用的字的字模提前讀取到內存里面加快速度,同時還可以使用ID3DXSprite渲染,進一步加快速度。雖然內部仍然有GDI的部分,不過很明顯工作方式發生了極大的變化。根據我的估計,這次的ID3DXFont很聰明的利用GDI獲得文字的輪廓,然后通過紋理來渲染。這樣的速度就快得多了,而且文字質量也得到了很好的控制,基本和直接用GDI的質量相同了。
              由于PreloadCharacters()和PreloadGlyphs()不是那么好理解,一般用PreloadText()就行。建議將所有ASCII字符、標點符號和部分漢字預讀進去。這個預讀過程略微有點慢,而且根據預讀的文字數量和你創建文字的字號,占用的內存也不同。這里給大家一堆文字,你Copy過去就行:

            引用

            const char strPreloadText[] = " 1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM~!@#$%^&*()-=[]\\;',./_+{}|:\"<>? 、。·ˉˇ¨〃—~‖…‘’“”〔〕〈〉《》「」『』〖〗【】!"#¥%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}我人有的和主產不為這工要在第一上是中國經已發了民同";

              注意第一個字符是空格哦!把空格預讀進去可是很重要的^_^
              看上去并不多,因為要考慮到內存占用及速度,我只預讀了一些符號和五筆的一鍵字。這些字符在24號字時候已經占用了快1MB了,比起PixelFont字庫占用的要大得多。天知道ID3DXFont到底預讀了些什么……
              PreloadText()的第二個參數不要用strlen,sizeof(strPreloadText)即可。
              然后就是利用ID3DXSprite來渲染。注意ID3DXFont::DrawText的第一個參數就是LPD3DXSPRITE,因此如果要利用ID3DXSprite,要將ID3DXFont::DrawText放到ID3DXSprite::Begin和ID3DXSprite::End之間。這就是我剛才說的不直接用ID3DXSprite的意思,ID3DXFont會完成ID3DXSprite的全部調用,你不用擔心。
              另外你應該注意到ID3DXSprite::Begin增加了參數,實際上DX文檔里面沒說,但是示例里面有,如果想讓ID3DXSprite發揮作用并且最大幅度的提升效率,參數上設定D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE即可。意思很明白:打開Alpha過濾和紋理篩選。這里DX文檔上有個錯誤一直沒改:文檔里給出的是D3DXSprite__SORT_TEXTURE,但是你可以試試,絕對報錯。
              剩下的就沒啥了,ID3DXFont的使用方法上一話已經講過。要注意的是D3DXCreateFont和D3DXCreateFontIndirect都發生了變化。D3DXCreateFont已經不再牽扯GDI了,D3DXCreateFontIndirect所使用的結構也變成了D3DXFONT_DESC,相對于LOGFONT結構,除去了一些用不著的參數,增加了一個MipLevels,就是MipMap等級啦,不用多說,2D下只用1。其他的上一話都有。實際上由于D3DXCreateFont已經不再關聯GDI,D3DXCreateFontIndirect的存在僅僅是由于歷史原因(為了兼容像我這種人的使用習慣),大家還是用D3DXCreateFont吧,省事。
              截圖就不貼了,沒啥意義。你可能覺得直接向后備緩沖上DrawText還不夠好看,那么就先畫到一張紋理上,然后將紋理錯位渲染到后備緩沖并且打開線型過濾,就可以達到和PixelFont相同的效果了。
              速度嘛……我畫了整整一屏幕字,在不緩沖文字的情況下(這個“緩沖文字”和ID3DXFont的文字緩沖可不是一回事啊!看過上一話的都應該知道我這里指的是什么),速度仍然在120FPS以上。或許你會覺得速度還是有點慢,但是,如果用D3D8的ID3DXFont畫上這么一屏幕,基本就只剩20FPS了。
              使用ID3DXFont替換掉PixelFont的優勢就是可以方便的自定義字體字號了,并且也不再受GB2312字庫的限制。所以大家都換了吧……都換了吧……把PixelFont忘了吧……

            『穩定的DX9 SDK版本』

              我現在用的是April 2006,而且應該會用很長時間。August 2006我是肯定不會去用啦!即使我不再恐懼D3D9,也會對這個SDK避讓三分的。其實對于2D,我感覺用到April 2006就足夠了,之后的DX9 SDK主要在D3DX的3D函數庫部分進行更改……其實也是秋后的螞蚱蹦達不了幾天,D3D10馬上就要出來了。要說D3D10啊……你還是看我另外一篇日志好了,總之打死我都不拿它做2D。

              實際上僅僅是2D的話,從D3D8轉向D3D9并沒有多少變化,主要是穩定嘛!只要你不調用一些D3D9專用的功能,即使拿D3D9來做2D,在絕大多數顯卡上還是能夠運行的。嗯……GF2等級以上吧,GF2之前的,也太老了,無視好了。

            《再上點菜好了:全屏幕模式》

              其實并不是多么復雜的問題,讓我拖了這么久……不拖了,這里就教給大家如何做全屏幕模式以及如何處理設備丟失的問題。

            『創建全屏幕模式』

              D3DPRESENT_PARAMS里面,Windowed設定為false,并且一定要設定BackBufferWidth和BackBufferHeight,完畢。
              哈哈,就這么簡單,或許早就有人嘗試過了,但是你試試按下Alt+Tab,再切換回去,保證你什么都看不到。
              之前曾經說過,DX8之前的版本,在全屏幕下工作比在窗口下容易,到DX8之后就則完全顛倒過來。因為在窗口模式下不用擔心設備丟失(除非你更改桌面分辨率),全屏幕模式下就會有這個問題了。下面詳述:

            『設備、資源丟失』

              設備丟失會發生在全屏幕模式下切換回桌面時(不論是通過Alt+Tab還是QQ上有人給你發了張圖片-_-bbb),而且如果在調用IDirect3DDevice9::Reset(從現在開始就是D3D9了啊!忘記D3D8吧……)的時候發生錯誤,設備也會丟失。
              設備丟失會造成資源丟失:所有創建在D3DPOOL_DEFAULT池的資源都會丟失,需要重新創建,其內容當然也會消失,需要重寫。
              然而創建在D3DPOOL_SYSTEMMEM和D3DPOOL_SCRATCH池的資源不會受到影響。創建在D3DPOOL_MANAGED池的資源也不會丟失,而且在設備重新可用的時候,D3DPOOL_MANAGED池的資源也可以立即投入使用,內容也不會改變。看這個池名字:托管池就能知道,D3D幫你處理了所有問題。
              因此避免設備丟失后資源丟失的簡易方法就是將所有資源創建在D3DPOOL_MANAGED池內。不過這并不是個好方法,這意味著不能用渲染對象——記得嗎?RenderTarget只能創建在D3DPOOL_DEFAULT。實際上最好的方法是跟蹤所有D3DPOOL_DEFAULT資源,比如利用std::list,將所有D3DPOOL_DEFAULT資源勾住,在設備發生丟失的時候釋放掉資源,設備可以繼續使用的時候重新創建資源,記得把數據寫回去。對于其他的池就不用這么折騰了。

            『當設備丟失之后』

              不論通過任何方式發生了設備丟失,所有的操作幾乎都會失效,只有Release()可以用——其實D3D會保證有部分操作可以成功,但是也僅僅是“可以”成功而不是“一定”成功,所以你還不如認定丟失的時候全都會失敗比較好——以及IDirect3DDevice9::TestCooperativeLevel。因此在設備丟失之后,你應該停止整個游戲循環,而通過反復調用IDirect3DDevice9::TestCooperativeLevel判斷設備是否可用。

            『IDirect3DDevice9::TestCooperativeLevel』

              這個方法檢測當前的設備狀態。返回值有四種:D3D_OK一切正常,D3DERR_DEVICELOST設備丟失,D3DERR_DEVICENOTRESET設備可以Reset。另外還有D3D9新增的D3DERR_DRIVERINTERNALERROR,遇到這個你就完蛋了,基本不可能恢復了,終止程序吧。
              按照順序來講,如果游戲在正常運行,D3D_OK會返回;如果發生了設備丟失并且在這個時候不能恢復,比如全屏幕模式的時候用戶切換到了Windows桌面,就會返回D3DERR_DEVICELOST;如果用戶又切換回了游戲,設備可以恢復了(還沒恢復呢!只是“可以”恢復而已),就會返回D3DERR_DEVICENOTRESET
              另外,IDirect3DDevice9::Present也會返回類似的值,不過你最好別指望這個,老老實實的用TestCooperativeLevel。因為Present在設備可以恢復的時候還是返回D3DERR_DEVICELOST(外一句:D3D10的時候TestCooperativeLevel就會完全整合到Present里面了,可喜可賀可喜可賀)

            『處理設備丟失』

              看下面的偽代碼:

            switch (IDirect3DDevice9::TestCooperativeLevel()){
              case D3D_OK:
                GameLoop();
                break;
              case D3DERR_DEVICELOST:
                break;
              case D3DERR_DEVICENOTRESET
                OnLostDevice();
                IDirect3DDevice9::Reset();
                OnResetDevice();
                break;
              default:
                QuitGame();
                break;
            }

              GameLoop()就是你的游戲運行的過程了。把這個switch寫在我們游戲框架的GameMain()部分,具體的位置可以看任何一話附帶的源代碼。
              好像我一直沒有講IDirect3DDevice9::Reset的參數啊?因為只有一個參數,就是指向D3DPRESENT_PARAMS的指針。把你第一次創建設備時使用的D3DPRESENT_PARAMS結構保存起來,供Reset來用。
              OnLostDevice()就是Release掉所有D3DPOOL_DEFAULT的資源,OnResetDevice()就是Create*()恢復啦!你可能注意到ID3DXFont、ID3DXSprite等等都有同名的方法,就是在這個時候調用的。如果你沒有這么做,也就是說還保留著任何D3DPOOL_DEFAULT的資源的話,IDirect3DDevice9::Reset就一定會失敗。
              另外在OnResetDevice里面你還要重新進行SetRenderState、SetSamplerState等等,Reset之后這些東西也丟失了。實際上Reset和重新創建一次設備類似,所不同的是重新創建設備的話你需要連D3DPOOL_MANAGED的資源也Release掉。這個話題就不討論了。
              從代碼可以看出來,D3DERR_DEVICELOST時程序什么都沒做,只是在傻等。我認為這是一個好習慣,因為實在不能保證在D3DERR_DEVICELOST時除了Release還能干什么,與其這樣還不如等設備能用了再說。

              實在懶得管資源的話,全部D3DPOOL_MANAGED好了。至于渲染對象?自己想辦法。

            『人工制造“設備丟失”』

              “干嘛還要制造設備丟失啊?”如果更改游戲分辨率、色深、切換全屏幕及窗口狀態,進行這樣的操作也要通過Reset,同樣的,Reset之前也要釋放掉所有D3DPOOL_DEFAULT資源(其實嚴格來說,還有更多的資源也要釋放,不過在2D下基本不會創建這類資源,你就不用管了)并且調用ID3DXSprite::OnLostDevice之類的方法。這就是人工制造“設備丟失”了。實際上在這個過程設備并沒有真正的丟失,只是會有一段時間處于不可用的狀態,此時Reset尚未返回,整個D3D設備就好像死了一樣。舉個例子,你切換桌面分辨率,會有那么一段時間顯示器上什么都不顯示,然后很快就正常了。和這個現象是同一個原因。Reset成功后記得恢復資源。
              你可能注意到這里的Reset和上面的Reset不是一回事。的確是這樣,這里是為了重設狀態而不是恢復設備。因此更改分辨率、色深的Reset需要寫到switch外面,也就是別和它攪和的意思-_-bb。而且你只需要OnLostDevice -> Reset -> OnResetDevice。記住:正確的調用Reset不會造成設備丟失,這個概念別弄混了。

            『切換全屏幕模式時的注意事項』

              注意WindowStyle的變化。切換成全屏幕模式后,只能使用WS_POPUP,不然顯示會變得怪怪的,你可以通過SetWindowLongPtr函數更改窗口外觀,第二個參數指定GWL_STYLE即可。別忘了WS_VISIBLE啊!不然你什么都看不見。

            『更詳細的文檔』

              我這里只是簡單討論了造成設備丟失的原因及處理方法,更詳細的內容你可以參考DX SDK文檔的Lost Device文章,人家是權威的。

            【以上,正片結束,后面是ED】

              我們前進到了D3D9,趕上了時代。
              我們創建了全屏幕游戲,趕上了時代。
              我卻變得一腦子漿糊,被觀眾拋棄了。
              哈哈,開玩笑啦,不過這一話很亂倒是真的,因為不論是更新到D3D9還是設備丟失,牽扯的東西都太散太雜,結果弄得這一話也是一盤散沙(居然又沒有附帶代碼)。唉,大家就忍了吧,忍不了的話就來PIA我吧。

              關于更新至D3D9更多的內容,你可以參考SDK文檔的《Converting to Direct3D 9》。

            【以上,ED結束,后面是……】

              第一季完結了……
              回過頭來看看,從第一話創建一個Windows窗口,到這一話的設備丟失,話題的層次一直在深入,現在已經深入到了不再是“學習”而是“研究”的范圍。我也不再想僅僅是搞“教學”而是想和大家“討論”。不過第一季主要還是教學吧。能堅持著看D2D教程到現在的,應該基本能夠寫出完整的2D Demo來了吧。如果有什么問題的話,歡迎提出,我在看到后會立刻回答的……只要你這個問題不太RP的話……
              那么,第二季會是什么樣子?
              第二季就不再是教學了,而開始我和大家的討論過程。第二季的第一話,也就是第09話,我將提供一些高級技巧給大家,并希望有興趣的朋友和我一起進行這些技巧的研究。另外在第二季里面,我們還要創建一個2D圖形引擎。原來打算給大家講解Medux 2,不過現在感覺這東西實在小兒科,絕對會讓大家B4的。那么既然如此,干脆介紹Mercury 3好了,有意見無?
              透漏一點下一話的內容吧:模糊精度和多次紋理渲染,嘿嘿,聽上去挺高深的是不?實際上超級簡單,就看你能不能想到而已。
              希望你在看完這一話之后,返回去再把前面的內容看看,相信你會得到新的收獲。搞不好你還能抓出幾個Bug呢!因為我是想到什么寫什么,沒個章法,Bug是難免的。
             


            附加:

            Direct3D中的字體與文本顯示
            圖形系統中為了獲得當前運行程序的相關信息,往往需要在屏幕上顯示文本,Direct3D的功能擴展接口ID3DXFont對此提供了方便的解決方法。

             

             

            創建ID3DXFont對象

            使用接口ID3DXFont繪制文本,首先需要通過函數D3DXCreateFont()創建ID3DXFont字體對象。ID3DXFont接口封裝了Windows字體和Direct3D設備指針,D3DXCreateFont()函數通過Windows字體和Direct3D設備指針創建ID3DXFont對象,該函數的聲明如下:

            Creates a font object for a device and font.

            HRESULT D3DXCreateFont(  LPDIRECT3DDEVICE9 pDevice,  INT Height,  UINT Width,  UINT Weight,  UINT MipLevels,  BOOL Italic,  DWORD CharSet,  DWORD OutputPrecision,  DWORD Quality,  DWORD PitchAndFamily,  LPCTSTR pFacename,  LPD3DXFONT * ppFont);
            Parameters
            pDevice
            [in] Pointer to an IDirect3DDevice9 interface, the device to be associated with the font object.
            Height
            [in] The height of the characters in logical units.
            Width
            [in] The width of the characters in logical units.
            Weight
            [in] Typeface weight. One example is bold.
            MipLevels
            [in] The number of mipmap levels.
            Italic
            [in] True for italic font, false otherwise.
            CharSet
            [in] The character set of the font.
            OutputPrecision
            [in] Specifies how Windows should attempt to match the desired font sizes and characteristics with actual fonts. Use OUT_TT_ONLY_PRECIS for instance, to ensure that you always get a TrueType font.
            Quality
            [in] Specifies how Windows should match the desired font with a real font. It applies to raster fonts only and should not affect TrueType fonts.
            PitchAndFamily
            [in] Pitch and family index.
            pFacename
            [in] String containing the typeface name. If the compiler settings require Unicode, the data type LPCTSTR resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See Remarks.
            ppFont
            [out] Returns a pointer to an ID3DXFont interface, representing the created font object.
            Return Values
            If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the following: D3DERR_INVALIDCALL, D3DXERR_INVALIDDATA, E_OUTOFMEMORY.

            Remarks
            The creation of an ID3DXFont object requires that the device supports 32-bit color.

            The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXCreateFontW. Otherwise, the function call resolves to D3DXCreateFontA because ANSI strings are being used.

            If you want more information about font parameters, see The Logical Font.

            示例代碼如下:

            D3DXCreateFont(g_device, 50, 20, 20, 0, FALSE, DEFAULT_CHARSET, 0, 0, 0, "Arial", &g_font);

            posted @ 2009-09-12 07:40 RedLight 閱讀(2388) | 評論 (0)編輯 收藏

            OpenGL中的選擇和反饋

            讀完此章之后,你將能夠做到:
              建立允許用戶選擇(select)屏幕區域或拾取(pick)繪制在屏幕上的物體的應用程序
              利用OpenGL的反饋(feedback)模式獲取絢染計算結果
              
              有些圖形應用程序只繪制兩維和三維物體構成的靜態圖形,另一些允許用戶識別屏幕上的物體并移動、修改、刪除或用其它方法操縱這些物體。OpenGL正是設計用于支持這些交互式應用程序的。因為繪制在屏幕上的物體通常經過多次旋轉、移動和透視變換,所以確定用戶選中了三維場景中的哪個物體會很困難。為了幫助你,OpenGL提供了一個選取機制可惟自動告訴你哪個物體被繪制在窗口的提定區域里。你可以用這個機制與一個工具例程(a special utility routine)一道決定哪個物體在用戶說明或用光標選取的區域里。
              
              選擇(selection)實際上是OpenGL的一個操作模式;反饋(feedback)是這類模式中的別一個。在反饋模式中,你用你的圖形硬件和OpenGL完成通常的絢染計算。但與用這個計算結果去在屏幕上繪制圖形相反,OpenGL返回(或反饋(feeds back))這些繪制信息給你。如果你想在繪圖儀而不是屏幕上繪制圖形,舉個例子,你就得在反饋模式繪制它們,收集繪制指令,然后將這些指令轉換為繪圖儀可以理解的命令。
              
              在選擇和反饋模式中,繪制信息返回給應用程序而不是象在絢染模式中那樣送往幀緩沖。因此,當OpenGL處于選擇或反饋模式時,屏幕將被凍結-沒有圖形出現。這一章將會在各自的節中解釋這些模式:
              
              “選擇(Selection)” 討論怎樣使用選擇模式和相關的例程以使你程序的用戶能拾取畫在屏幕上的物體。
              
              “反饋(Feedback)” 描述了怎樣獲取有關什么將被畫在屏幕上的信息和這些信息是以什么格式組織的。
              
              ---------------------------------------------------
              Section
              
              通常,當你打算使用OpenGL的選擇機制時,你首先把你的場景畫進幀緩沖,然后進入選擇模式并重新繪制這個場景。然而,一旦你進入了選擇模式,幀緩沖的內容將保存不變,直到你退出選擇模式。當你退出時,OpenGL返回一個圖元(premitives)清單,圖元可能被視見體(viewing volume)分割(記住,視見體是由當前模式視見和投影矩陣及你定義的所有裁剪面定義,裁剪面詳見"Additional Clipping Planes.")。每個被視見體圖元引出一資選擇命中(hit)。確切的說,圖元清單是作為一個取整數值的名字(integer-valued names)數組和相關的數據-命中記錄(hit record)-對應名字棧(name stack)的當前內容。當你在選擇模式下發布圖元繪制命令時向名字棧中加入名字就可建立起名字棧。這樣,當名字清單被返回后,你就可以用它來確定屏幕上的哪個圖元可能被用戶選中了。
              
              除了這個選擇機制之外,OpenGL提供了一個工具例程,以便在某些情況下通過限定在視口(viewport)一個小區域內繪制來簡化選擇。通常你可以用這個例程決定哪個物體被畫在光標附近了,這樣你就能識別用戶拾取了哪個物體。你也可以通過指定附加的裁剪面來界定一個選擇區域;詳見"Additional Clipping Planes"。因為拾取是選擇的一個特殊情況,所以本章選講選擇,然后講拾取。
              
              基本步驟
              建立名字矩陣
              命中記錄
              一個選擇的例子
              拾取
              關于編寫使用選擇的程序的提示
              
              ------------------------------------------------------------------------
              基本步驟
              
              為使用選擇機制,你得作以下幾步:
              1、用glSelectBuffer()指定用于返回命中記錄的數組。
              2、以GL_SELECT為參數調用glRenderMode()進入選擇模式。
              3、用glInitName()和glPushName()初始化名字棧。
              4、定義用于選擇的視見體。通常它與你原來用于繪制場景的視見體不同。因此你或許會想用glPushMatrix()和glPopMatrix()來保存和恢復當前的變換矩陣。
              5、交替發布圖元繪制命令和名字棧控制命令,這樣每個感興趣的圖元都會被指定適當的名字。
              6、退出選擇模式并處理返回的選擇數據(命中記錄)。
              
              后面的段落將描述glSelectBuffer()和glRenderMode()。下一節則講解名字棧的控制。
              
              void glSelectBuffer(GLsizei size, GLuint *buffer);
              指定用于返回選擇數據的數組。參數buffer是指向無符號整數(unsigned integer)數組的指針,數據就存在這個數組中,size參數說明數組中最多能夠保存的值的個數。要在進入選擇模式之前調用glSelectBuffer()!
              
              GLint glRenderMode(GLenum mode);
              控制應用程序是否進入絢染(rendering)、選擇或反饋模式。mode參數可以是GL_RENDER(默認)、GL_SELECT或GL_FEEDBACK之一。應用程序將保持處于給定模式,直到再次以不同的參數調用glRenderMode()。在進入選擇模式之前必須調用glSelectBuffer()指定選擇數組。類似的,進入反饋模式之前要調用glFeedbackBuffer()指定反饋數組。如果當前模式是GL_SELECT或GL_FEEDBACK之一,那么glRenderMode()的返回值有意義。返回值是當前退出當前模式時,選擇命中數或放在反饋數組中的值的個數。(譯者注:調用此函數就會退出當前模式);負值意味著選擇或反饋數組溢出(overflowed)。你可以用GL_RENDER_MODE調用glGetIntegerv()獲取當前模式。
              
              -------------------------------------------------------------------------------
              建立名字矩陣
              
              正如前面提到的,名字棧是返回給你的選擇信息的基礎。要建立名字棧,首先用glInitNames()初始化它,這將簡單地清空棧。然后當你發布相應的繪制命令時向其中加入整數名字。正如你可能想象,棧控制命令允許你壓入名字(glPushName()),彈出名字(glPopName()),替換棧頂的名字(glLoadName())。
              /********************************************************************/
              Example 12-1: Creating a Name Stack
              glInitNames();
              glPushName(-1);
              
              glPushMatrix(); /* save the current transformation state */
              
              /*to do: create your desired viewing volume here */
              
              glLoadName(1);
              drawSomeObject();
              glLoadName(2);
              drawAnotherObject();
              glLoadName(3);
              drawYetAnotherObject();
              drawJustOneMoreObject();
              
              glPopMatrix (); /* restore the previous transformation state*/
              /********************************************************************/
              
              在這個例子中,前兩個被繪制的物體有自己的名字,第三和第四個共用一個名字。這樣,如果第三或第四個物體中的一個或全部引起一個選擇命中,只有一個命中記錄返回給你。如果處理命中記錄時不想區分各個物體的話,可以讓多個物體共享一個名字。
              
              void glInitNames(void);
              清空名字棧。
              
              void glPushName(GLuint name);
              將name壓入名字棧。壓入名字超過棧容量時將生成一個GL_STACK_OVERFLOW錯誤。名字棧深度因OpenGL實現(implementations)不同而不同,但最少要能容納64個名字。你可以用參數GL_NAME_STACK_DEPTH調用glGetIntegerv()以獲取名字棧深度。
              
              void glPopName(void);
              彈出名字棧棧頂的那一個名字。從空棧中彈出名字引發GL_STACK_UNDERFLOW錯誤。
              
              void glLoadName(GLuint name);
              用name取代名字棧棧頂的那個名字。如果棧是空的,剛調用過glInitName()后就是這樣,glLoadName()生成一個GL_INVALID_OPRATION錯。為避免這種情況,如果棧初始時是空的,那么在調用glLoadName()之前至少調用一次glPushName()以在名字棧中放上點東西。
              
              如果不是在選擇模式下,對glPushName()、glPopName()、glLoadName()的調用將被忽略。這使得在選擇模式和正常的絢染模式下用相同的繪制代碼大為簡化。
              
              -------------------------------------------------------------------------------
              命中記錄
              
              在選擇模式下,被視見體裁剪的每個圖元引起一個選擇命中。當前一個名字棧控制命令被執行或glRenderMode()被調用后,OpenGL將一個命中記錄寫進選擇數組,如果從上一次名字棧操縱或glRenderMode()調用以來有了一個命中記錄的話。這個過程中,共用同樣名字的物體-例如:由多個圖元組成的物體-不生成多個命中記錄。當然,命中記錄不保證會被寫進數組中直到glRenderMode()被調用。
              
              除圖元之外,glRasterPos()產生的有效坐標也可以引起選擇命中。在多邊形的情況下,如果它已經被消隱掉的話不會有命中記錄出現。
              
              每個命中記錄由四項組成,依次是:
              當命中出現時名字棧中的名字數
              至上次記錄的命中以來,被視見體裁剪后的圖元的所有頂點的窗口Z坐標的 最大和最小值
              本次命中時名字棧的內容,最底元素最前。
              
              當前你進入選擇模式時,OpenGL初始化一個指針指向選擇數組的起點。每寫入一個命中記錄,指針相應更新。如果寫入一個命中記錄會使數組中值的個數超過glSelectBuffer()的size參數時,OpenGL會寫入盡可能多的記錄并設置一個溢出標志。當用glRenderMode()退出選擇模式時,這條命令返回被寫入的記錄的個數(包括一條部分記錄如果有的話),清除名字棧,復位溢出標識,重置棧指針。如設定溢了出標識則返回值是-1。

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/Crazyjumper/archive/2007/10/18/1830865.aspx

            posted @ 2009-09-01 06:28 RedLight 閱讀(519) | 評論 (0)編輯 收藏

            OpenGL中的Alpha測試,深度測試,模板測試,裁減測試

            大家好。現在因為參加工作的關系,又是長時間沒有更新。趁著國慶的空閑,總算是又寫出了一課。我感覺入門的知識已經快要介紹完畢,這課之后再有一課,就可以告一段落了。以后我可能會寫一些自己在這方面的體會,做一份進階課程。

            現在即將放出的是第十二課的內容。

            首先還是以前課程的連接:

            第一課,編寫第一個OpenGL程序
            第二課,繪制幾何圖形
            第三課,繪制幾何圖形的一些細節問題
            第四課,顏色的選擇
            第五課,三維的空間變換
            第六課,動畫的制作
            第七課,使用光照來表現立體感
            第八課,使用顯示列表
            第九課,使用混合來實現半透明效果
            第十課,BMP文件與像素操作
            第十一課,紋理的使用入門
            第十二課,OpenGL片斷測試  ——→  本次課程的內容

            片斷測試其實就是測試每一個像素,只有通過測試的像素才會被繪制,沒有通過測試的像素則不進行繪制。OpenGL提供了多種測試操作,利用這些操作可以實現一些特殊的效果。
            我們在前面的課程中,曾經提到了“深度測試”的概念,它在繪制三維場景的時候特別有用。在不使用深度測試的時候,如果我們先繪制一個距離較近的物體,再繪制距離較遠的物體,則距離遠的物體因為后繪制,會把距離近的物體覆蓋掉,這樣的效果并不是我們所希望的。
            如果使用了深度測試,則情況就會有所不同:每當一個像素被繪制,OpenGL就記錄這個像素的“深度”(深度可以理解為:該像素距離觀察者的距離。深度值越大,表示距離越遠),如果有新的像素即將覆蓋原來的像素時,深度測試會檢查新的深度是否會比原來的深度值小。如果是,則覆蓋像素,繪制成功;如果不是,則不會覆蓋原來的像素,繪制被取消。這樣一來,即使我們先繪制比較近的物體,再繪制比較遠的物體,則遠的物體也不會覆蓋近的物體了。
            實際上,只要存在深度緩沖區,無論是否啟用深度測試,OpenGL在像素被繪制時都會嘗試將深度數據寫入到緩沖區內,除非調用了glDepthMask(GL_FALSE)來禁止寫入。這些深度數據除了用于常規的測試外,還可以有一些有趣的用途,比如繪制陰影等等。

            除了深度測試,OpenGL還提供了剪裁測試、Alpha測試和模板測試。

            1、剪裁測試
            剪裁測試用于限制繪制區域。我們可以指定一個矩形的剪裁窗口,當啟用剪裁測試后,只有在這個窗口之內的像素才能被繪制,其它像素則會被丟棄。換句話說,無論怎么繪制,剪裁窗口以外的像素將不會被修改。
            有的朋友可能玩過《魔獸爭霸3》這款游戲。游戲時如果選中一個士兵,則畫面下方的一個方框內就會出現該士兵的頭像。為了保證該頭像無論如何繪制都不會越界而覆蓋到外面的像素,就可以使用剪裁測試。

            可以通過下面的代碼來啟用或禁用剪裁測試:
            glEnable(GL_SCISSOR_TEST);  // 啟用剪裁測試
            glDisable(GL_SCISSOR_TEST); // 禁用剪裁測試

            可以通過下面的代碼來指定一個位置在(x, y),寬度為width,高度為height的剪裁窗口。
            glScissor(x, y, width, height);

            注意,OpenGL窗口坐標是以左下角為(0, 0),右上角為(width, height)的,這與Windows系統窗口有所不同。

            還有一種方法可以保證像素只繪制到某一個特定的矩形區域內,這就是視口變換(在第五課第3節中有介紹)。但視口變換和剪裁測試是不同的。視口變換是將所有內容縮放到合適的大小后,放到一個矩形的區域內;而剪裁測試不會進行縮放,超出矩形范圍的像素直接忽略掉。


            2、Alpha測試
            在前面的課程中,我們知道像素的Alpha值可以用于混合操作。其實Alpha值還有一個用途,這就是Alpha測試。當每個像素即將繪制時,如果啟動了Alpha測試,OpenGL會檢查像素的Alpha值,只有Alpha值滿足條件的像素才會進行繪制(嚴格的說,滿足條件的像素會通過本項測試,進行下一種測試,只有所有測試都通過,才能進行繪制),不滿足條件的則不進行繪制。這個“條件”可以是:始終通過(默認情況)、始終不通過、大于設定值則通過、小于設定值則通過、等于設定值則通過、大于等于設定值則通過、小于等于設定值則通過、不等于設定值則通過。
            如果我們需要繪制一幅圖片,而這幅圖片的某些部分又是透明的(想象一下,你先繪制一幅相片,然后繪制一個相框,則相框這幅圖片有很多地方都是透明的,這樣就可以透過相框看到下面的照片),這時可以使用Alpha測試。將圖片中所有需要透明的地方的Alpha值設置為0.0,不需要透明的地方Alpha值設置為1.0,然后設置Alpha測試的通過條件為:“大于0.5則通過”,這樣便能達到目的。當然也可以設置需要透明的地方Alpha值為1.0,不需要透明的地方Alpha值設置為0.0,然后設置條件為“小于0.5則通過”。Alpha測試的設置方式往往不只一種,可以根據個人喜好和實際情況需要進行選擇。

            可以通過下面的代碼來啟用或禁用Alpha測試:


            glEnable(GL_ALPHA_TEST);  // 啟用Alpha測試
            glDisable(GL_ALPHA_TEST); // 禁用Alpha測試


            可以通過下面的代碼來設置Alpha測試條件為“大于0.5則通過”:


            glAlphaFunc(GL_GREATER, 0.5f);


            該函數的第二個參數表示設定值,用于進行比較。第一個參數是比較方式,除了GL_LESS(小于則通過)外,還可以選擇:
            GL_ALWAYS(始終通過),
            GL_NEVER(始終不通過),
            GL_LESS(小于則通過),
            GL_LEQUAL(小于等于則通過),
            GL_EQUAL(等于則通過),
            GL_GEQUAL(大于等于則通過),
            GL_NOTEQUAL(不等于則通過)。
            現在我們來看一個實際例子。一幅照片圖片,一幅相框圖片,如何將它們組合在一起呢?為了簡單起見,我們使用前面兩課一直使用的24位BMP文件來作為圖片格式。(因為發布到網絡上,為了節約容量,我所發布的是JPG格式。大家下載后可以用Windows XP自帶的畫圖工具打開,并另存為24位BMP格式)

            注:第一幅圖片是著名網絡游戲《魔獸世界》的一幅桌面背景,用在這里希望沒有涉及版權問題。如果有什么不妥,請及時指出,我會立即更換。

            在24位的BMP文件格式中,BGR三種顏色各占8位,沒有保存Alpha值,因此無法直接使用Alpha測試。注意到相框那幅圖片中,所有需要透明的位置都是白色,所以我們在程序中設置所有白色(或很接近白色)的像素Alpha值為0.0,設置其它像素Alpha值為1.0,然后設置Alpha測試的條件為“大于0.5則通過”即可。這種使用某種特殊顏色來代表透明顏色的技術,有時又被成為Color Key技術。
            利用前面第11課的一段代碼,將圖片讀取為紋理,然后利用下面這個函數來設置“當前紋理”中每一個像素的Alpha值。


            /* 將當前紋理BGR格式轉換為BGRA格式
             * 紋理中像素的RGB值如果與指定rgb相差不超過absolute,則將Alpha設置為0.0,否則設置為1.0
             */
            void texture_colorkey(GLubyte r, GLubyte g, GLubyte b, GLubyte absolute)
            {
                GLint width, height;
                GLubyte* pixels = 0;

                // 獲得紋理的大小信息
                glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
                glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);

                // 分配空間并獲得紋理像素
                pixels = (GLubyte*)malloc(width*height*4);
                if( pixels == 0 )
                    return;
                glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);

                // 修改像素中的Alpha值
                // 其中pixels[i*4], pixels[i*4+1], pixels[i*4+2], pixels[i*4+3]
                //   分別表示第i個像素的藍、綠、紅、Alpha四種分量,0表示最小,255表示最大
                {
                    GLint i;
                    GLint count = width * height;
                    for(i=0; i<count; ++i)
                    {
                        if( abs(pixels[i*4] - b) <= absolute
                         && abs(pixels[i*4+1] - g) <= absolute
                         && abs(pixels[i*4+2] - r) <= absolute )
                            pixels[i*4+3] = 0;
                        else
                            pixels[i*4+3] = 255;
                    }
                }

                // 將修改后的像素重新設置到紋理中,釋放內存
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
                    GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
                free(pixels);
            }

            有了紋理后,我們開啟紋理,指定合適的紋理坐標并繪制一個矩形,這樣就可以在屏幕上將圖片繪制出來。我們先繪制相片的紋理,再繪制相框的紋理。程序代碼如下:


            void display(void)
            {
                static int initialized   = 0;
                static GLuint texWindow  = 0;
                static GLuint texPicture = 0;

                // 執行初始化操作,包括:讀取相片,讀取相框,將相框由BGR顏色轉換為BGRA,啟用二維紋理
                if( !initialized )
                {
                    texPicture = load_texture("pic.bmp");
                    texWindow  = load_texture("window.bmp");
                    glBindTexture(GL_TEXTURE_2D, texWindow);
                    texture_colorkey(255, 255, 255, 10);

                    glEnable(GL_TEXTURE_2D);

                    initialized = 1;
                }

                // 清除屏幕
                glClear(GL_COLOR_BUFFER_BIT);

                // 繪制相片,此時不需要進行Alpha測試,所有的像素都進行繪制
                glBindTexture(GL_TEXTURE_2D, texPicture);
                glDisable(GL_ALPHA_TEST);
                glBegin(GL_QUADS);
                    glTexCoord2f(0, 0);     glVertex2f(-1.0f, -1.0f);
                    glTexCoord2f(0, 1);     glVertex2f(-1.0f,  1.0f);
                    glTexCoord2f(1, 1);     glVertex2f( 1.0f,  1.0f);
                    glTexCoord2f(1, 0);     glVertex2f( 1.0f, -1.0f);
                glEnd();

                // 繪制相框,此時進行Alpha測試,只繪制不透明部分的像素
                glBindTexture(GL_TEXTURE_2D, texWindow);
                glEnable(GL_ALPHA_TEST);
                glAlphaFunc(GL_GREATER, 0.5f);
                glBegin(GL_QUADS);
                    glTexCoord2f(0, 0);     glVertex2f(-1.0f, -1.0f);
                    glTexCoord2f(0, 1);     glVertex2f(-1.0f,  1.0f);
                    glTexCoord2f(1, 1);     glVertex2f( 1.0f,  1.0f);
                    glTexCoord2f(1, 0);     glVertex2f( 1.0f, -1.0f);
                glEnd();

                // 交換緩沖
                glutSwapBuffers();
            }

            其中:load_texture函數是從第11課中照搬過來的(該函數還使用了一個power_of_two函數,一個BMP_Header_Length常數,同樣照搬),無需進行修改。main函數跟其它課程的基本相同,不再重復。
            程序運行后,會發現相框與相片的銜接有些不自然,這是因為相框某些邊緣部分雖然肉眼看上去是白色,但其實RGB值與純白色相差并不少,因此程序計算其Alpha值時認為其不需要透明。解決辦法是仔細處理相框中的每個像素,在需要透明的地方涂上純白色,這也許是一件很需要耐心的工作。


                大家可能會想:前面我們學習過混合操作,混合可以實現半透明,自然也可以通過設定實現全透明。也就是說,Alpha測試可以實現的效果幾乎都可以通過OpenGL混合功能來實現。那么為什么還需要一個Alpha測試呢?答案就是,這與性能相關。Alpha測試只要簡單的比較大小就可以得到最終結果,而混合操作一般需要進行乘法運算,性能有所下降。另外,OpenGL測試的順序是:剪裁測試、Alpha測試、模板測試、深度測試。如果某項測試不通過,則不會進行下一步,而只有所有測試都通過的情況下才會執行混合操作。因此,在使用Alpha測試的情況下,透明的像素就不需要經過模板測試和深度測試了;而如果使用混合操作,即使透明的像素也需要進行模板測試和深度測試,性能會有所下降。還有一點:對于那些“透明”的像素來說,如果使用Alpha測試,則“透明”的像素不會通過測試,因此像素的深度值不會被修改;而使用混合操作時,雖然像素的顏色沒有被修改,但它的深度值則有可能被修改掉了。
            因此,如果所有的像素都是“透明”或“不透明”,沒有“半透明”時,應該盡量采用Alpha測試而不是采用混合操作。當需要繪制半透明像素時,才采用混合操作。

               3、模板測試
            模板測試是所有OpenGL測試中比較復雜的一種。

            首先,模板測試需要一個模板緩沖區,這個緩沖區是在初始化OpenGL時指定的。如果使用GLUT工具包,可以在調用glutInitDisplayMode函數時在參數中加上GLUT_STENCIL,例如:


            glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL);

            在Windows操作系統中,即使沒有明確要求使用模板緩沖區,有時候也會分配模板緩沖區。但為了保證程序的通用性,最好還是明確指定使用模板緩沖區。如果確實沒有分配模板緩沖區,則所有進行模板測試的像素全部都會通過測試。

            通過glEnable/glDisable可以啟用或禁用模板測試。


            glEnable(GL_STENCIL_TEST);  // 啟用模板測試
            glDisable(GL_STENCIL_TEST); // 禁用模板測試


            OpenGL在模板緩沖區中為每個像素保存了一個“模板值”,當像素需要進行模板測試時,將設定的模板參考值與該像素的“模板值”進行比較,符合條件的通過測試,不符合條件的則被丟棄,不進行繪制。
            條件的設置與Alpha測試中的條件設置相似。但注意Alpha測試中是用浮點數來進行比較,而模板測試則是用整數來進行比較。比較也有八種情況:始終通過、始終不通過、大于則通過、小于則通過、大于等于則通過、小于等于則通過、等于則通過、不等于則通過。


            glStencilFunc(GL_LESS, 3, mask);

            這段代碼設置模板測試的條件為:“小于3則通過”。glStencilFunc的前兩個參數意義與glAlphaFunc的兩個參數類似,第三個參數的意義為:如果進行比較,則只比較mask中二進制為1的位。例如,某個像素模板值為5(二進制101),而mask的二進制值為00000011,因為只比較最后兩位,5的最后兩位為01,其實是小于3的,因此會通過測試。

            如何設置像素的“模板值”呢?glClear函數可以將所有像素的模板值復位。代碼如下:


            glClear(GL_STENCIL_BUFFER_BIT);

            可以同時復位顏色值和模板值:


            glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

            正如可以使用glClearColor函數來指定清空屏幕后的顏色那樣,也可以使用glClearStencil函數來指定復位后的“模板值”。

            每個像素的“模板值”會根據模板測試的結果和深度測試的結果而進行改變。


            glStencilOp(fail, zfail, zpass);

            該函數指定了三種情況下“模板值”該如何變化。第一個參數表示模板測試未通過時該如何變化;第二個參數表示模板測試通過,但深度測試未通過時該如何變化;第三個參數表示模板測試和深度測試均通過時該如何變化。如果沒有起用模板測試,則認為模板測試總是通過;如果沒有啟用深度測試,則認為深度測試總是通過)
            變化可以是:
            GL_KEEP(不改變,這也是默認值),
            GL_ZERO(回零),
            GL_REPLACE(使用測試條件中的設定值來代替當前模板值),
            GL_INCR(增加1,但如果已經是最大值,則保持不變),
            GL_INCR_WRAP(增加1,但如果已經是最大值,則從零重新開始),
            GL_DECR(減少1,但如果已經是零,則保持不變),
            GL_DECR_WRAP(減少1,但如果已經是零,則重新設置為最大值),
            GL_INVERT(按位取反)。

            在新版本的OpenGL中,允許為多邊形的正面和背面使用不同的模板測試條件和模板值改變方式,于是就有了glStencilFuncSeparate函數和glStencilOpSeparate函數。這兩個函數分別與glStencilFunc和glStencilOp類似,只在最前面多了一個參數face,用于指定當前設置的是哪個面。可以選擇GL_FRONT, GL_BACK, GL_FRONT_AND_BACK。

            注意:模板緩沖區與深度緩沖區有一點不同。無論是否啟用深度測試,當有像素被繪制時,總會重新設置該像素的深度值(除非設置glDepthMask(GL_FALSE);)。而模板測試如果不啟用,則像素的模板值會保持不變,只有啟用模板測試時才有可能修改像素的模板值。(這一結論是我自己的實驗得出的,暫時沒發現什么資料上是這樣寫。如果有不正確的地方,歡迎指正)
            另外,模板測試雖然是從OpenGL 1.0就開始提供的功能,但是對于個人計算機而言,硬件實現模板測試的似乎并不多,很多計算機系統直接使用CPU運算來完成模板測試。因此在一些老的顯卡,或者是多數集成顯卡上,大量而頻繁的使用模板測試可能造成程序運行效率低下。即使是當前配置比較高端的個人計算機,也盡量不要使用glStencilFuncSeparate和glStencilOpSeparate函數。

            從前面所講可以知道,使用剪裁測試可以把繪制區域限制在一個矩形的區域內。但如果需要把繪制區域限制在一個不規則的區域內,則需要使用模板測試。
            例如:繪制一個湖泊,以及周圍的樹木,然后繪制樹木在湖泊中的倒影。為了保證倒影被正確的限制在湖泊表面,可以使用模板測試。具體的步驟如下:
            (1) 關閉模板測試,繪制地面和樹木。
            (2) 開啟模板測試,使用glClear設置所有像素的模板值為0。
            (3) 設置glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);繪制湖泊水面。這樣一來,湖泊水面的像素的“模板值”為1,而其它地方像素的“模板值”為0。
            (4) 設置glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);繪制倒影。這樣一來,只有“模板值”為1的像素才會被繪制,因此只有“水面”的像素才有可能被倒影的像素替換,而其它像素則保持不變。


               我們仍然來看一個實際的例子。這是一個比較簡單的場景:空間中有一個球體,一個平面鏡。我們站在某個特殊的觀察點,可以看到球體在平面鏡中的鏡像,并且鏡像處于平面鏡的邊緣,有一部分因為平面鏡大小的限制,而無法顯示出來。整個場景的效果如下圖:


            繪制這個場景的思路跟前面提到的湖面倒影是接近的。
            假設平面鏡所在的平面正好是X軸和Y軸所確定的平面,則球體和它在平面鏡中的鏡像是關于這個平面對稱的。我們用一個draw_sphere函數來繪制球體,先調用該函數以繪制球體本身,然后調用glScalef(1.0f, 1.0f, -1.0f); 再調用draw_sphere函數,就可以繪制球體的鏡像。
            另外需要注意的地方就是:因為是繪制三維的場景,我們開啟了深度測試。但是站在觀察者的位置,球體的鏡像其實是在平面鏡的“背后”,也就是說,如果按照常規的方式繪制,平面鏡會把鏡像覆蓋掉,這不是我們想要的效果。解決辦法就是:設置深度緩沖區為只讀,繪制平面鏡,然后設置深度緩沖區為可寫的狀態,繪制平面鏡“背后”的鏡像。
            有的朋友可能會問:如果在繪制鏡像的時候關閉深度測試,那鏡像不就不會被平面鏡遮擋了嗎?為什么還要開啟深度測試,又需要把深度緩沖區設置為只讀呢?實際情況是:雖然關閉深度測試確實可以讓鏡像不被平面鏡遮擋,但是鏡像本身會出現若干問題。我們看到的鏡像是一個球體,但實際上這個球體是由很多的多邊形所組成的,這些多邊形有的代表了我們所能看到的“正面”,有的則代表了我們不能看到的“背面”。如果關閉深度測試,而有的“背面”多邊形又比“正面”多邊形先繪制,就會造成球體的背面反而把正面擋住了,這不是我們想要的效果。為了確保正面可以擋住背面,應該開啟深度測試。
            繪制部分的代碼如下:


            void draw_sphere()
            {
                // 設置光源
                glEnable(GL_LIGHTING);
                glEnable(GL_LIGHT0);
                {
                    GLfloat
                        pos[]     = {5.0f, 5.0f, 0.0f, 1.0f},
                        ambient[] = {0.0f, 0.0f, 1.0f, 1.0f};
                    glLightfv(GL_LIGHT0, GL_POSITION, pos);
                    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
                }

                // 繪制一個球體
                glColor3f(1, 0, 0);
                glPushMatrix();
                glTranslatef(0, 0, 2);
                glutSolidSphere(0.5, 20, 20);
                glPopMatrix();
            }

            void display(void)
            {
                // 清除屏幕
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

                // 設置觀察點
                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
                gluPerspective(60, 1, 5, 25);
                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                gluLookAt(5, 0, 6.5, 0, 0, 0, 0, 1, 0);

                glEnable(GL_DEPTH_TEST);

                // 繪制球體
                glDisable(GL_STENCIL_TEST);
                draw_sphere();

                // 繪制一個平面鏡。在繪制的同時注意設置模板緩沖。
                // 另外,為了保證平面鏡之后的鏡像能夠正確繪制,在繪制平面鏡時需要將深度緩沖區設置為只讀的。
                // 在繪制時暫時關閉光照效果
                glClearStencil(0);
                glClear(GL_STENCIL_BUFFER_BIT);
                glStencilFunc(GL_ALWAYS, 1, 0xFF);
                glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
                glEnable(GL_STENCIL_TEST);

                glDisable(GL_LIGHTING);
                glColor3f(0.5f, 0.5f, 0.5f);
                glDepthMask(GL_FALSE);
                glRectf(-1.5f, -1.5f, 1.5f, 1.5f);
                glDepthMask(GL_TRUE);

                // 繪制一個與先前球體關于平面鏡對稱的球體,注意光源的位置也要發生對稱改變
                // 因為平面鏡是在X軸和Y軸所確定的平面,所以只要Z坐標取反即可實現對稱
                // 為了保證球體的繪制范圍被限制在平面鏡內部,使用模板測試
                glStencilFunc(GL_EQUAL, 1, 0xFF);
                glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
                glScalef(1.0f, 1.0f, -1.0f);
                draw_sphere();

                // 交換緩沖
                glutSwapBuffers();

                // 截圖
                grab();
            }


            其中display函數的末尾調用了一個grab函數,它保存當前的圖象到一個BMP文件。這個函數本來是在第十課和第十一課中都有所使用的。但是我發現它有一個bug,現在進行了修改:在函數最開頭的部分加上一句:glReadBuffer(GL_FRONT);即可。注意這個函數最好是在繪制完畢后(如果是使用雙緩沖,則應該在交換緩沖后)立即調用。


              大家可能會有這樣的感覺:模板測試的設置是如此復雜,它可以實現的功能應該很多,肯定不止這樣一個“限制像素的繪制范圍”。事實上也是如此,不過現在我們暫時只講這些。

            其實,如果不需要繪制半透明效果,有時候可以用混合功能來代替模板測試。就繪制鏡像這個例子來說,可以采用下面的步驟:
            (1) 清除屏幕,在glClearColor中設置合適的值確保清除屏幕后像素的Alpha值為0.0
            (2) 關閉混合功能,繪制球體本身,設置合適的顏色(或者光照與材質)以確保所有被繪制的像素的Alpha值為0.0
            (3) 繪制平面鏡,設置合適的顏色(或者光照與材質)以確保所有被繪制的像素的Alpha值為1.0
            (4) 啟用混合功能,用GL_DST_ALPHA作為源因子,GL_ONE_MINUS_DST_ALPHA作為目標因子,這樣就實現了只有原來Alpha為1.0的像素才能被修改,而原來Alpha為0.0的像素則保持不變。這時再繪制鏡像物體,注意確保所有被繪制的像素的Alpha值為1.0。
            在有的OpenGL實現中,模板測試是軟件實現的,而混合功能是硬件實現的,這時候可以考慮這樣的代替方法以提高運行效率。但是并非所有的模板測試都可以用混合功能來代替,并且這樣的代替顯得不自然,復雜而且容易出錯。
            另外始終注意:使用混合來模擬時,即使某個像素原來的Alpha值為0.0,以致于在繪制后其顏色不會有任何變化,但是這個像素的深度值有可能會被修改,而如果是使用模板測試,沒有通過測試的像素其深度值不會發生任何變化。而且,模板測試和混合功能中,像素模板值的修改方式是不一樣的。
              

              4、深度測試
            在本課的開頭,已經簡單的敘述了深度測試。這里是完整的內容。

            深度測試需要深度緩沖區,跟模板測試需要模板緩沖區是類似的。如果使用GLUT工具包,可以在調用glutInitDisplayMode函數時在參數中加上GLUT_DEPTH,這樣來明確指定要求使用深度緩沖區。
            深度測試和模板測試的實現原理很類似,都是在一個緩沖區保存像素的某個值,當需要進行測試時,將保存的值與另一個值進行比較,以確定是否通過測試。兩者的區別在于:模板測試是設定一個值,在測試時用這個設定值與像素的“模板值”進行比較,而深度測試是根據頂點的空間坐標計算出深度,用這個深度與像素的“深度值”進行比較。也就是說,模板測試需要指定一個值作為比較參考,而深度測試中,這個比較用的參考值是OpenGL根據空間坐標自動計算的。

            通過glEnable/glDisable函數可以啟用或禁用深度測試。
            glEnable(GL_DEPTH_TEST);  // 啟用深度測試
            glDisable(GL_DEPTH_TEST); // 禁用深度測試

            至于通過測試的條件,同樣有八種,與Alpha測試中的條件設置相同。條件設置是通過glDepthFunc函數完成的,默認值是GL_LESS。
            glDepthFunc(GL_LESS);

            與模板測試相比,深度測試的應用要頻繁得多。幾乎所有的三維場景繪制都使用了深度測試。正因為這樣,幾乎所有的OpenGL實現都對深度測試提供了硬件支持,所以雖然兩者的實現原理類似,但深度測試很可能會比模板測試快得多。當然了,兩種測試在應用上很少有交集,一般不會出現使用一種測試去代替另一種測試的情況。


               小結:
            本次課程介紹了OpenGL所提供的四種測試,分別是剪裁測試、Alpha測試、模板測試、深度測試。OpenGL會對每個即將繪制的像素進行以上四種測試,每個像素只有通過一項測試后才會進入下一項測試,而只有通過所有測試的像素才會被繪制,沒有通過測試的像素會被丟棄掉,不進行繪制。每種測試都可以單獨的開啟或者關閉,如果某項測試被關閉,則認為所有像素都可以順利通過該項測試。
            剪裁測試是指:只有位于指定矩形內部的像素才能通過測試。
            Alpha測試是指:只有Alpha值與設定值相比較,滿足特定關系條件的像素才能通過測試。
            模板測試是指:只有像素模板值與設定值相比較,滿足特定關系條件的像素才能通過測試。
            深度測試是指:只有像素深度值與新的深度值比較,滿足特定關系條件的像素才能通過測試。
            上面所說的特定關系條件可以是大于、小于、等于、大于等于、小于等于、不等于、始終通過、始終不通過這八種。
            模板測試需要模板緩沖區,深度測試需要深度緩沖區。這些緩沖區都是在初始化OpenGL時指定的。如果使用GLUT工具包,則可以在glutInitDisplayMode函數中指定。無論是否開啟深度測試,OpenGL在像素被繪制時都會嘗試修改像素的深度值;而只有開啟模板測試時,OpenGL才會嘗試修改像素的模板值,模板測試被關閉時,OpenGL在像素被繪制時也不會修改像素的模板值。
            利用這些測試操作可以控制像素被繪制或不被繪制,從而實現一些特殊效果。利用混合功能可以實現半透明,通過設置也可以實現完全透明,因而可以模擬像素顏色的繪制或不繪制。但注意,這里僅僅是顏色的模擬。OpenGL可以為像素保存顏色、深度值和模板值,利用混合實現透明時,像素顏色不發生變化,但深度值則會可能變化,模板值受glStencilFunc函數中第三個參數影響;利用測試操作實現透明時,像素顏色不發生變化,深度值也不發生變化,模板值受glStencilFunc函數中前兩個參數影響。
            此外,修正了第十課、第十一課中的一個函數中的bug。在grab函數中,應該在最開頭加上一句glReadBuffer(GL_FRONT);以保證讀取到的內容正好就是顯示的內容。

             

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/Crazyjumper/archive/2007/12/26/1968567.aspx

            posted @ 2009-09-01 06:27 RedLight 閱讀(623) | 評論 (0)編輯 收藏

            優化3D圖形渲染通道負載(轉)

            一般來說, 定位渲染通道瓶頸的方法就是改變渲染通道每個步驟的工作量, 如果吞吐量也改變了, 那個步驟就是瓶頸.。找到了瓶頸就要想辦法消除瓶頸, 可以減少該步驟的工作量, 增加其他步驟的工作量。

               一般在光柵化之前的瓶頸稱作”transform bound”, 三角形設置處理后的瓶頸稱作”fill bound”定位瓶頸的辦法:
            • 1.改變幀緩沖或者渲染目標(Render Target)的顏色深度(16 到 32 位), 如果幀速改變了, 那么瓶頸應該在幀緩沖(RenderTarget)的填充率上。
            • 2.否則試試改變貼圖大小和貼圖過濾設置, 如果幀速變了,那么瓶頸應該是在貼圖這里。
            • 3.否則改變分辨率.如果幀速改變了, 那么改變一下pixel shader的指令數量, 如果幀速變了, 那么瓶頸應該就是pixel shader. 否則瓶頸就在光柵化過程中。
            • 4.否則, 改變頂點格式的大小, 如果幀速改變了, 那么瓶頸應該在顯卡帶寬上。
            • 5.如果以上都不是, 那么瓶頸就在CPU這一邊。
            • 優化方法36條:
            • 1.盡量減少無用的頂點數據, 比如貼圖坐標, 如果有Object使用2組有的使用1組, 那么不 要將他們放在一個vertex buffer中, 這樣可以減少傳輸的數據量。
            • 2.使用多個streamsource, 比如SkinMesh渲染, 可以把頂點坐標和法線這些每一幀都要修改的數據放在一個動態VB中, 其它不需要修改的(如貼圖坐標)放到一個靜態VB中, 這樣就減少了數據傳輸量。
            • 3.盡量使用16位的索引緩沖,避免32位的. 一方面浪費帶寬, 一方面也不是所有的顯卡都支持32位的索引緩沖。
            • 4.可以考慮使用vertex shader來計算靜態VB中的數據.比如SkinMesh的頂點可以放到vectex shader中計算, 這樣就可以避免每一幀都從AGP內存中向顯存傳送數據. 這樣也可以使用靜態VB了。
            • 5.堅決避免使用Draw**UP一族的函數來繪制多邊形。
            • 6.在設計程序之前好好規劃一下顯卡內存的使用, 確保framebuffer, 貼圖, 靜態VB能夠正好放入顯卡的本地內存中。
            • 7.盡量使頂點格式大小是32字節的倍數.可以考慮使用壓縮過的頂點格式然后用vertex shader去解. 或者留下冗余的部分, 使頂點大小剛好使32字節的倍數。
            • 8.頂點在頂點緩沖中的順序盡量符合繪制的順序, 考慮使用strips來代替list。
            • 9.如果可能盡量多的使用static vertex buffer代替dynamic vertex buffer。
            • 10.動態VB使用DISCARD參數來lock更新, 使用NOOVERWRITE來添加.盡量不要使用不帶參數的lock調用(0)。
            • 11.盡量減少lock的次數, 有些東西并不一定非要每一幀都更新VB, 比如人物動畫一般每秒鐘更新30次VB基本上就夠了。
            • 12.如果是因為需要繪制的頂點數據太多了可以考慮使用LOD, 但是現在的顯卡的繪制能力都很強勁, 所以需要權衡一下LOD是否能夠帶來相應的好處, 如果過分的強化LOD很可能將瓶頸轉移到CPU這邊。
            • 13.避免過多的頂點計算,比如過多的光源, 過于復雜的光照計算(復雜的光照模型), 紋理自動生成的開啟也會增加頂點的計算量. 如果貼圖坐標變換矩陣不是單位矩陣, 也會造成頂點計算量的增加, 所以如果紋理變換已經結束, 記得要將紋理變換矩陣設為單位矩陣同時調整貼圖坐標。
            • 14.避免Vertex shader指令數量太多或者分支過多, 盡量減少vertex shader的長度和復雜程度. 盡量使用swizzling代替mov。
            • 15.如果圖象質量方面的計算(pixel shader)范圍很大, 并且很復雜, 可以考慮試試全屏反走樣。說不定更快。
            • 16.盡量按照front – back的順序來繪制。
            • 17.在shader中判斷Z值可以避免繪制不可見的象素, 但是nvidia建議簡單的shader不要這么做.(Don't do this in a simple shader)。
            • 18.如果可能, 盡量使用vertex shader來代替pixel shader.將計算從逐象素變成逐頂點。
            • 19.盡量降低貼圖的大小.過大的貼圖可能造成貼圖cache過載, 從而導致貼圖cache命中降低.過大的貼圖會導致顯存過載, 這時候貼圖是從系統內存中取的。
            • 20.只要可能就用16位色的貼圖, 如環境貼圖或者shadow map.它們用32位色的貼圖實在是浪費。
            • 21.考慮使用DXT 貼圖壓縮。
            • 22.如果可能,使用簡單的貼圖過濾或者mip map, 除非必要否則盡量不要使用三線過濾和各項異性過濾. light map 和 環境貼圖基本上都不需要使用它們。
            • 23.只有真正需要修改的貼圖才使用Dynamic, 并且使用DISCRAD和WRITEONLY來lock。
            • 24.太多的幀緩沖讀寫可以考慮關閉Z-Writes如有些多pass的渲染中的后續pass或者粒子系統等半透明幾何物體(如果可以)。
            • 25.可能的話盡量使用alpha test代替alpha blending。
            • 26.如果不需要stencil buffer就盡量使用16位的Z buffer。
            • 27.減小RenderTarget 貼圖的大小, 如shadow map 環境貼圖. 可能根本不需要那么大效果就很好。
            • 28.Stencil 和 Z buffer 盡量一起clear. 他們本來就是一塊緩沖。
            • 29.盡量減少渲染狀態的切換, 盡量一次畫盡可能多的多邊形。(根據顯卡性能決定最多畫多少, 不過一般再多也不會多到哪里去。 除非你根本不需要貼圖和渲染狀態的切換)。
            • 30.盡量使用shader來代替Fixed Pipeline。
            • 31.盡量使用shader來實現來取代Multipass渲染效果。
            • 32.盡量優先先建立重要的資源, 如Render target, shaders, 貼圖, VB, IB等等.以免顯存過載的時候它們被創建到系統內存中。
            • 33.堅決不要在渲染循環中調用創建資源。
            • 34.按照shader和貼圖分組后再渲染.先按照shaders分組再按貼圖。
            • 35.Color Stencil Z buffer盡量在一次Clear調用中清除。
            • 36.一個Vertex buffer 的大小在2M-4M之間最好。

            posted @ 2009-09-01 01:32 RedLight 閱讀(579) | 評論 (0)編輯 收藏

            OpenGL Performance Optimization(轉)

            SIGGRAPH '97

            Course 24: OpenGL and Window System Integration

            OpenGL Performance Optimization



            Contents



            1. Hardware vs. Software

            OpenGL may be implemented by any combination of hardware and software. At the high-end, hardware may implement virtually all of OpenGL while at the low-end, OpenGL may be implemented entirely in software. In between are combination software/hardware implementations. More money buys more hardware and better performance.

            Intro-level workstation hardware and the recent PC 3-D hardware typically implement point, line, and polygon rasterization in hardware but implement floating point transformations, lighting, and clipping in software. This is a good strategy since the bottleneck in 3-D rendering is usually rasterization and modern CPU's have sufficient floating point performance to handle the transformation stage.

            OpenGL developers must remember that their application may be used on a wide variety of OpenGL implementations. Therefore one should consider using all possible optimizations, even those which have little return on the development system, since other systems may benefit greatly.

            From this point of view it may seem wise to develop your application on a low-end system. There is a pitfall however; some operations which are cheep in software may be expensive in hardware. The moral is: test your application on a variety of systems to be sure the performance is dependable.



            2. Application Organization

            At first glance it may seem that the performance of interactive OpenGL applications is dominated by the performance of OpenGL itself. This may be true in some circumstances but be aware that the organization of the application is also significant.

            2.1 High Level Organization

            Multiprocessing

            Some graphical applications have a substantial computational component other than 3-D rendering. Virtual reality applications must compute object interactions and collisions. Scientific visualization programs must compute analysis functions and graphical representations of data.

            One should consider multiprocessing in these situations. By assigning rendering and computation to different threads they may be executed in parallel on multiprocessor computers.

            For many applications, supporting multiprocessing is just a matter of partitioning the render and compute operations into separate threads which share common data structures and coordinate with synchronization primitives.

            SGI's Performer is an example of a high level toolkit designed for this purpose.

            Image quality vs. performance

            In general, one wants high-speed animation and high-quality images in an OpenGL application. If you can't have both at once a reasonable compromise may be to render at low complexity during animation and high complexity for static images.

            Complexity may refer to the geometric or rendering attributes of a database. Here are a few examples.

            • During interactive rotation (i.e. mouse button held down) render a reduced-polygon model. When drawing a static image draw the full polygon model.
            • During animation, disable dithering, smooth shading, and/or texturing. Enable them for the static image.
            • If texturing is required, use GL_NEAREST sampling and glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST ).
            • During animation, disable antialiasing. Enable antialiasing for the static image.
            • Use coarser NURBS/evaluator tesselation during animation. Use glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ) to inspect tesselation granularity and reduce if possible.

            Level of detail management and culling

            Objects which are distant from the viewer may be rendered with a reduced complexity model. This strategy reduces the demands on all stages of the graphics pipeline. Toolkits such as Inventor and Performer support this feature automatically.

            Objects which are entirely outside of the field of view may be culled. This type of high level cull testing can be done efficiently with bounding boxes or spheres and have a major impact on performance. Again, toolkits such as Inventor and Performer have this feature.

            2.2 Low Level Organization

            The objects which are rendered with OpenGL have to be stored in some sort of data structure. Some data structures are more efficient than others with respect to how quickly they can be rendered.

            Basically, one wants data structures which can be traversed quickly and passed to the graphics library in an efficient manner. For example, suppose we need to render a triangle strip. The data structure which stores the list of vertices may be implemented with a linked list or an array. Clearly the array can be traversed more quickly than a linked list. The way in which a vertex is stored in the data structure is also significant. High performance hardware can process vertexes specified by a pointer more quickly than those specified by three separate parameters.

            An Example

            Suppose we're writing an application which involves drawing a road map. One of the components of the database is a list of cities specified with a latitude, longitude and name. The data structure describing a city may be:
            	struct city {
            float latitute, longitude;	/* city location */
            char *name;			/* city's name */
            int large_flag;  		/* 0 = small, 1 = large */
            };
            
            A list of cities may be stored as an array of city structs.

            Our first attempt at rendering this information may be:

            	void draw_cities( int n, struct city citylist[] )
            {
            int i;
            for (i=0; i < n; i++) {
            if (citylist[i].large_flag) {
            glPointSize( 4.0 );
            }
            else {
            glPointSize( 2.0 );
            }
            glBegin( GL_POINTS );
            glVertex2f( citylist[i].longitude, citylist[i].latitude );
            glEnd();
            glRasterPos2f( citylist[i].longitude, citylist[i].latitude );
            glCallLists( strlen(citylist[i].name),
            GL_BYTE,
            citylist[i].name );
            }
            }
            
            This is a poor implementation for a number of reasons:
            • glPointSize is called for every loop iteration.
            • only one point is drawn between glBegin and glEnd
            • the vertices aren't being specified in the most efficient manner
            Here's a better implementation:
            	void draw_cities( int n, struct city citylist[] )
            {
            int i;
            /* draw small dots first */
            glPointSize( 2.0 );
            glBegin( GL_POINTS );
            for (i=0; i < n ;i++) {
            if (citylist[i].large_flag==0) {
            glVertex2f( citylist[i].longitude, citylist[i].latitude );
            }
            }
            glEnd();
            /* draw large dots second */
            glPointSize( 4.0 );
            glBegin( GL_POINTS );
            for (i=0; i < n ;i++) {
            if (citylist[i].large_flag==1) {
            glVertex2f( citylist[i].longitude, citylist[i].latitude );
            }
            }
            glEnd();
            /* draw city labels third */
            for (i=0; i < n ;i++) {
            glRasterPos2f( citylist[i].longitude, citylist[i].latitude );
            glCallLists( strlen(citylist[i].name),
            GL_BYTE,
            citylist[i].name );
            }
            }
            
            In this implementation we're only calling glPointSize twice and we're maximizing the number of vertices specified between glBegin and glEnd.

            We can still do better, however. If we redesign the data structures used to represent the city information we can improve the efficiency of drawing the city points. For example:

            	struct city_list {
            int num_cities;		/* how many cities in the list */
            float *position;	/* pointer to lat/lon coordinates */
            char **name;		/* pointer to city names */
            float size;		/* size of city points */
            };
            
            Now cities of different sizes are stored in separate lists. Position are stored sequentially in a dynamically allocated array. By reorganizing the data structures we've eliminated the need for a conditional inside the glBegin/glEnd loops. Also, we can render a list of cities using the GL_EXT_vertex_array extension if available, or at least use a more efficient version of glVertex and glRasterPos.
            	/* indicates if server can do GL_EXT_vertex_array: */
            GLboolean varray_available;
            void draw_cities( struct city_list *list )
            {
            int i;
            GLboolean use_begin_end;
            /* draw the points */
            glPointSize( list->size );
            #ifdef GL_EXT_vertex_array
            if (varray_available) {
            glVertexPointerEXT( 2, GL_FLOAT, 0, list->num_cities, list->position );
            glDrawArraysEXT( GL_POINTS, 0, list->num_cities );
            use_begin_end = GL_FALSE;
            }
            else
            #else
            {
            use_begin_end = GL_TRUE;
            }
            #endif
            if (use_begin_end) {
            glBegin(GL_POINTS);
            for (i=0; i < list->num_cities; i++) {
            glVertex2fv( &position[i*2] );
            }
            glEnd();
            }
            /* draw city labels */
            for (i=0; i < list->num_cities ;i++) {
            glRasterPos2fv( list->position[i*2] );
            glCallLists( strlen(list->name[i]),
            GL_BYTE, list->name[i] );
            }
            }
            
            As this example shows, it's better to know something about efficient rendering techniques before designing the data structures. In many cases one has to find a compromize between data structures optimized for rendering and those optimized for clarity and convenience.

            In the following sections the techniques for maximizing performance, as seen above, are explained.



            3. OpenGL Optimization

            There are many possibilities to improving OpenGL performance. The impact of any single optimization can vary a great deal depending on the OpenGL implementation. Interestingly, items which have a large impact on software renderers may have no effect on hardware renderers, and vice versa! For example, smooth shading can be expensive in software but free in hardware While glGet* can be cheap in software but expensive in hardware.

            After each of the following techniques look for a bracketed list of symbols which relates the significance of the optimization to your OpenGL system:

            • H - beneficial for high-end hardware
            • L - beneficial for low-end hardware
            • S - beneficial for software implementations
            • all - probably beneficial for all implementations

            3.1 Traversal

            Traversal is the sending of data to the graphics system. Specifically, we want to minimize the time taken to specify primitives to OpenGL.
            Use connected primitives
            Connected primitives such as GL_LINES, GL_LINE_LOOP, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, and GL_QUAD_STRIP require fewer vertices to describe an object than individual line, triangle, or polygon primitives. This reduces data transfer and transformation workload. [all]
            Use the vertex array extension
            On some architectures function calls are somewhat expensive so replacing many glVertex/glColor/glNormal calls with the vertex array mechanism may be very beneficial. [all]
            Store vertex data in consecutive memory locations
            When maximum performance is needed on high-end systems it's good to store vertex data in contiguous memory to maximize through put of data from host memory to graphics subsystem. [H,L]
            Use the vector versions of glVertex, glColor, glNormal and glTexCoord
            The glVertex, glColor, etc. functions which take a pointer to their arguments such as glVertex3fv(v) may be much faster than those which take individual arguments such as glVertex3f(x,y,z) on systems with DMA-driven graphics hardware. [H,L]
            Reduce quantity of primitives
            Be careful not to render primitives which are over-tesselated. Experiment with the GLU primitives, for example, to determine the best compromise of image quality vs. tesselation level. Textured objects in particular may still be rendered effectively with low geometric complexity. [all]
            Display lists
            Use display lists to encapsulate frequently drawn objects. Display list data may be stored in the graphics subsystem rather than host memory thereby eliminating host-to-graphics data movement. Display lists are also very beneficial when rendering remotely. [all]
            Don't specify unneeded per-vertex information
            If lighting is disabled don't call glNormal. If texturing is disabled don't call glTexCoord, etc.
            Minimize code between glBegin/glEnd
            For maximum performance on high-end systems it's extremely important to send vertex data to the graphics system as fast as possible. Avoid extraneous code between glBegin/glEnd.

            Example:

            	glBegin( GL_TRIANGLE_STRIP );
            for (i=0; i < n; i++) {
            if (lighting) {
            glNormal3fv( norm[i] );
            }
            glVertex3fv( vert[i] );
            }
            glEnd();
            

            This is a very bad construct. The following is much better:

            	if (lighting) {
            glBegin( GL_TRIANGLE_STRIP );
            for (i=0; i < n ;i++) {
            glNormal3fv( norm[i] );
            glVertex3fv( vert[i] );
            }
            glEnd();
            }
            else {
            glBegin( GL_TRIANGLE_STRIP );
            for (i=0; i < n ;i++) {
            glVertex3fv( vert[i] );
            }
            glEnd();
            }
            
            Also consider manually unrolling important rendering loops to maximize the function call rate.

            3.2 Transformation

            Transformation includes the transformation of vertices from glVertex to window coordinates, clipping and lighting.

            Lighting
            • Avoid using positional lights, i.e. light positions should be of the form (x,y,z,0) [L,S]
            • Avoid using spotlights. [all]
            • Avoid using two-sided lighting. [all]
            • Avoid using negative material and light color coefficients [S]
            • Avoid using the local viewer lighting model. [L,S]
            • Avoid frequent changes to the GL_SHININESS material parameter. [L,S]
            • Some OpenGL implementations are optimized for the case of a single light source.
            • Consider pre-lighting complex objects before rendering, ala radiosity. You can get the effect of lighting by specifying vertex colors instead of vertex normals. [S]
            Two sided lighting
            If you want both the front and back of polygons shaded the same try using two light sources instead of two-sided lighting. Position the two light sources on opposite sides of your object. That way, a polygon will always be lit correctly whether it's back or front facing. [L,S]
            Disable normal vector normalization when not needed
            glEnable/Disable(GL_NORMALIZE) controls whether normal vectors are scaled to unit length before lighting. If you do not use glScale you may be able to disable normalization without ill effects. Normalization is disabled by default. [L,S]
            Use connected primitives
            Connected primitives such as GL_LINES, GL_LINE_LOOP, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, and GL_QUAD_STRIP decrease traversal and transformation load.
            glRect usage
            If you have to draw many rectangles consider using glBegin(GL_QUADS) ... glEnd() instead. [all]

            3.3 Rasterization

            Rasterization is the process of generating the pixels which represent points, lines, polygons, bitmaps and the writing of those pixels to the frame buffer. Rasterization is often the bottleneck in software implementations of OpenGL.
            Disable smooth shading when not needed
            Smooth shading is enabled by default. Flat shading doesn't require interpolation of the four color components and is usually faster than smooth shading in software implementations. Hardware may perform flat and smooth-shaded rendering at the same rate though there's at least one case in which smooth shading is faster than flat shading (E&S Freedom). [S]
            Disable depth testing when not needed
            Background objects, for example, can be drawn without depth testing if they're drawn first. Foreground objects can be drawn without depth testing if they're drawn last. [L,S]
            Disable dithering when not needed
            This is easy to forget when developing on a high-end machine. Disabling dithering can make a big difference in software implementations of OpenGL on lower-end machines with 8 or 12-bit color buffers. Dithering is enabled by default. [S]
            Use back-face culling whenever possible.
            If you're drawing closed polyhedra or other objects for which back facing polygons aren't visible there's probably no point in drawing those polygons. [all]
            The GL_SGI_cull_vertex extension
            SGI's Cosmo GL supports a new culling extension which looks at vertex normals to try to improve the speed of culling.
            Avoid extra fragment operations
            Stenciling, blending, stippling, alpha testing and logic ops can all take extra time during rasterization. Be sure to disable the operations which aren't needed. [all]
            Reduce the window size or screen resolution
            A simple way to reduce rasterization time is to reduce the number of pixels drawn. If a smaller window or reduced display resolution are acceptable it's an easy way to improve rasterization speed. [L,S]

            3.4 Texturing

            Texture mapping is usually an expensive operation in both hardware and software. Only high-end graphics hardware can offer free to low-cost texturing. In any case there are several ways to maximize texture mapping performance.
            Use efficient image formats
            The GL_UNSIGNED_BYTE component format is typically the fastest for specifying texture images. Experiment with the internal texture formats offered by the GL_EXT_texture extension. Some formats are faster than others on some systems (16-bit texels on the Reality Engine, for example). [all]
            Encapsulate texture maps in texture objects or display lists
            This is especially important if you use several texture maps. By putting textures into display lists or texture objects the graphics system can manage their storage and minimize data movement between the client and graphics subsystem. [all]
            Use smaller texture maps
            Smaller images can be moved from host to texture memory faster than large images. More small texture can be stored simultaneously in texture memory, reducing texture memory swapping. [all]
            Use simpler sampling functions
            Experiment with the minification and magnification texture filters to determine which performs best while giving acceptable results. Generally, GL_NEAREST is fastest and GL_LINEAR is second fastest. [all]
            Use the same sampling function for minification and magnification
            If both the minification and magnification filters are GL_NEAREST or GL_LINEAR then there's no reason OpenGL has to compute the lambda value which determines whether to use minification or magnification sampling for each fragment. Avoiding the lambda calculation can be a good performace improvement.
            Use a simpler texture environment function
            Some texture environment modes may be faster than others. For example, the GL_DECAL or GL_REPLACE_EXT functions for 3 component textures is a simple assignment of texel samples to fragments while GL_MODULATE is a linear interpolation between texel samples and incoming fragments. [S,L]
            Combine small textures
            If you are using several small textures consider tiling them together as a larger texture and modify your texture coordinates to address the subtexture you want. This technique can eliminate texture bindings.
            Use glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)
            This hint can improve the speed of texturing when perspective- correct texture coordinate interpolation isn't needed, such as when using a glOrtho() projection.
            Animated textures
            If you want to use an animated texture, perhaps live video textures, don't use glTexImage2D to repeatedly change the texture. Use glTexSubImage2D or glTexCopyTexSubImage2D. These functions are standard in OpenGL 1.1 and available as extensions to 1.0.

            3.5 Clearing

            Clearing the color, depth, stencil and accumulation buffers can be time consuming, especially when it has to be done in software. There are a few tricks which can help.
            Use glClear carefully [all]
            Clear all relevant color buffers with one glClear.

            Wrong:

              glClear( GL_COLOR_BUFFER_BIT );
            if (stenciling) {
            glClear( GL_STENCIL_BUFFER_BIT );
            }
            
            Right:

              if (stenciling) {
            glClear( GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
            }
            else {
            glClear( GL_COLOR_BUFFER_BIT );
            }
            
            Disable dithering
            Disable dithering before clearing the color buffer. Visually, the difference between dithered and undithered clears is usually negligable.
            Use scissoring to clear a smaller area
            If you don't need to clear the whole buffer use glScissor() to restrict clearing to a smaller area. [L].
            Don't clear the color buffer at all
            If the scene you're drawing opaquely covers the entire window there is no reason to clear the color buffer.
            Eliminate depth buffer clearing
            If the scene you're drawing covers the entire window there is a trick which let's you omit the depth buffer clear. The idea is to only use half the depth buffer range for each frame and alternate between using GL_LESS and GL_GREATER as the depth test function.

            Example:

               int EvenFlag;
            /* Call this once during initialization and whenever the window
            * is resized.
            */
            void init_depth_buffer( void )
            {
            glClearDepth( 1.0 );
            glClear( GL_DEPTH_BUFFER_BIT );
            glDepthRange( 0.0, 0.5 );
            glDepthFunc( GL_LESS );
            EvenFlag = 1;
            }
            /* Your drawing function */
            void display_func( void )
            {
            if (EvenFlag) {
            glDepthFunc( GL_LESS );
            glDepthRange( 0.0, 0.5 );
            }
            else {
            glDepthFunc( GL_GREATER );
            glDepthRange( 1.0, 0.5 );
            }
            EvenFlag = !EvenFlag;
            /* draw your scene */
            }
            
            Avoid glClearDepth( d ) where d!=1.0
            Some software implementations may have optimized paths for clearing the depth buffer to 1.0. [S]

            3.6 Miscellaneous

            Avoid "round-trip" calls
            Calls such as glGetFloatv, glGetIntegerv, glIsEnabled, glGetError, glGetString require a slow, round trip transaction between the application and renderer. Especially avoid them in your main rendering code.

            Note that software implementations of OpenGL may actually perform these operations faster than hardware systems. If you're developing on a low-end system be aware of this fact. [H,L]

            Avoid glPushAttrib
            If only a few pieces of state need to be saved and restored it's often faster to maintain the information in the client program. glPushAttrib( GL_ALL_ATTRIB_BITS ) in particular can be very expensive on hardware systems. This call may be faster in software implementations than in hardware. [H,L]
            Check for GL errors during development
            During development call glGetError inside your rendering/event loop to catch errors. GL errors raised during rendering can slow down rendering speed. Remove the glGetError call for production code since it's a "round trip" command and can cause delays. [all]
            Use glColorMaterial instead of glMaterial
            If you need to change a material property on a per vertex basis, glColorMaterial may be faster than glMaterial. [all]
            glDrawPixels
            • glDrawPixels often performs best with GL_UNSIGNED_BYTE color components [all]
            • Disable all unnecessary raster operations before calling glDrawPixels. [all]
            • Use the GL_EXT_abgr extension to specify color components in alpha, blue, green, red order on systems which were designed for IRIS GL. [H,L].
            Avoid using viewports which are larger than the window
            Software implementations may have to do additional clipping in this situation. [S]
            Alpha planes
            Don't allocate alpha planes in the color buffer if you don't need them. Specifically, they are not needed for transparency effects. Systems without hardware alpha planes may have to resort to a slow software implementation. [L,S]
            Accumulation, stencil, overlay planes
            Do not allocate accumulation, stencil or overlay planes if they are not needed. [all]
            Be aware of the depth buffer's depth
            Your OpenGL may support several different sizes of depth buffers- 16 and 24-bit for example. Shallower depth buffers may be faster than deep buffers both for software and hardware implementations. However, the precision of of a 16-bit depth buffer may not be sufficient for some applications. [L,S]
            Transparency may be implemented with stippling instead of blending
            If you need simple transparent objects consider using polygon stippling instead of alpha blending. The later is typically faster and may actually look better in some situations. [L,S]
            Group state changes together
            Try to mimimize the number of GL state changes in your code. When GL state is changed, internal state may have to be recomputed, introducing delays. [all]
            Avoid using glPolygonMode
            If you need to draw many polygon outlines or vertex points use glBegin with GL_POINTS, GL_LINES, GL_LINE_LOOP or GL_LINE_STRIP instead as it can be much faster. [all]

            3.7 Window System Integration

            Minimize calls to the make current call
            The glXMakeCurrent call, for example, can be expensive on hardware systems because the context switch may involve moving a large amount of data in and out of the hardware.
            Visual / pixel format performance
            Some X visuals or pixel formats may be faster than others. On PCs for example, 24-bit color buffers may be slower to read/write than 12 or 8-bit buffers. There is often a tradeoff between performance and quality of frame buffer configurations. 12-bit color may not look as nice as 24-bit color. A 16-bit depth buffer won't have the precision of a 24-bit depth buffer.

            The GLX_EXT_visual_rating extension can help you select visuals based on performance or quality. GLX 1.2's visual caveat attribute can tell you if a visual has a performance penalty associated with it.

            It may be worthwhile to experiment with different visuals to determine if there's any advantage of one over another.

            Avoid mixing OpenGL rendering with native rendering
            OpenGL allows both itself and the native window system to render into the same window. For this to be done correctly synchronization is needed. The GLX glXWaitX and glXWaitGL functions serve this purpose.

            Synchronization hurts performance. Therefore, if you need to render with both OpenGL and native window system calls try to group the rendering calls to minimize synchronization.

            For example, if you're drawing a 3-D scene with OpenGL and displaying text with X, draw all the 3-D elements first, call glXWaitGL to synchronize, then call all the X drawing functions.

            Don't redraw more than necessary
            Be sure that you're not redrawing your scene unnecissarily. For example, expose/repaint events may come in batches describing separate regions of the window which must be redrawn. Since one usually redraws the whole window image with OpenGL you only need to respond to one expose/repaint event. In the case of X, look at the count field of the XExposeEvent structure. Only redraw when it is zero.

            Also, when responding to mouse motion events you should skip extra motion events in the input queue. Otherwise, if you try to process every motion event and redraw your scene there will be a noticable delay between mouse input and screen updates.

            It can be a good idea to put a print statement in your redraw and event loop function so you know exactly what messages are causing your scene to be redrawn, and when.

            SwapBuffer calls and graphics pipe blocking
            On systems with 3-D graphics hardware the SwapBuffers call is synchronized to the monitor's vertical retrace. Input to the OpenGL command queue may be blocked until the buffer swap has completed. Therefore, don't put more OpenGL calls immediately after SwapBuffers. Instead, put application computation instructions which can overlap with the buffer swap delay.

            3.8 Mesa-specific

            Mesa is a free library which implements most of the OpenGL API in a compatible manner. Since it is a software library, performance depends a great deal on the host computer. There are several Mesa-specific features to be aware of which can effect performance.

            Double buffering
            The X driver supports two back color buffer implementations: Pixmaps and XImages. The MESA_BACK_BUFFER environment variable controls which is used. Which of the two that's faster depends on the nature of your rendering. Experiment.
            X Visuals
            As described above, some X visuals can be rendered into more quickly than others. The MESA_RGB_VISUAL environment variable can be used to determine the quickest visual by experimentation.
            Depth buffers
            Mesa may use a 16 or 32-bit depth buffer as specified in the src/config.h configuration file. 16-bit depth buffers are faster but may not offer the precision needed for all applications.
            Flat-shaded primitives
            If one is drawing a number of flat-shaded primitives all of the same color the glColor command should be put before the glBegin call.

            Don't do this:

            	glBegin(...);
            glColor(...);
            glVertex(...);
            ...
            glEnd();
            

            Do this:

            	glColor(...);
            glBegin(...);
            glVertex(...);
            ...
            glEnd();
            
            glColor*() commands
            The glColor[34]ub[v] are the fastest versions of the glColor command.
            Avoid double precision valued functions
            Mesa does all internal floating point computations in single precision floating point. API functions which take double precision floating point values must convert them to single precision. This can be expensive in the case of glVertex, glNormal, etc.


            4. Evaluation and Tuning

            To maximize the performance of an OpenGL applications one must be able to evaluate an application to learn what is limiting its speed. Because of the hardware involved it's not sufficient to use ordinary profiling tools. Several different aspects of the graphics system must be evaluated.

            Performance evaluation is a large subject and only the basics are covered here. For more information see "OpenGL on Silicon Graphics Systems".

            4.1 Pipeline tuning

            The graphics system can be divided into three subsystems for the purpose of performance evaluation:
            • CPU subsystem - application code which drives the graphics subsystem
            • Geometry subsystem - transformation of vertices, lighting, and clipping
            • Rasterization subsystem - drawing filled polygons, line segments and per-pixel processing
            At any given time, one of these stages will be the bottleneck. The bottleneck must be reduced to improve performance. The strategy is to isolate each subsystem in turn and evaluate changes in performance. For example, by decreasing the workload of the CPU subsystem one can determine if the CPU or graphics system is limiting performance.

            4.1.1 CPU subsystem

            To isosulate the CPU subsystem one must reduce the graphics workload while presevering the application's execution characteristics. A simple way to do this is to replace glVertex() and glNormal calls with glColor calls. If performance does not improve then the CPU stage is the bottleneck.

            4.1.2 Geometry subsystem

            To isoslate the geometry subsystem one wants to reduce the number of primitives processed, or reduce the transformation work per primitive while producing the same number of pixels during rasterization. This can be done by replacing many small polygons with fewer large ones or by simply disabling lighting or clipping. If performance increases then your application is bound by geometry/transformation speed.

            4.1.3 Rasterization subsystem

            A simple way to reduce the rasterization workload is to make your window smaller. Other ways to reduce rasterization work is to disable per-pixel processing such as texturing, blending, or depth testing. If performance increases, your program is fill limited.

            After bottlenecks have been identified the techniques outlined in section 3 can be applied. The process of identifying and reducing bottlenecks should be repeated until no further improvements can be made or your minimum performance threshold has been met.

            4.2 Double buffering

            For smooth animation one must maintain a high, constant frame rate. Double buffering has an important effect on this. Suppose your application needs to render at 60Hz but is only getting 30Hz. It's a mistake to think that you must reduce rendering time by 50% to achive 60Hz. The reason is the swap-buffers operation is synchronized to occur during the display's vertical retrace period (at 60Hz for example). It may be that your application is taking only a tiny bit too long to meet the 1/60 second rendering time limit for 60Hz.

            Measure the performance of rendering in single buffer mode to determine how far you really are from your target frame rate.

            4.3 Test on several implementations

            The performance of OpenGL implementations varies a lot. One should measure performance and test OpenGL applications on several different systems to be sure there are no unexpected problems.


            posted @ 2009-08-25 06:05 RedLight 閱讀(892) | 評論 (0)編輯 收藏

            游戲Entity設計不完全整理(轉)

            在游戲引擎中,Entity通常被翻譯成實體,也常用諸如GameObjectActorSimulationObjectUnitCharacter等名字。相比于對圖像聲音引擎的熱情,Entity層多年來一直備受冷遇,但最近幾年隨著大型游戲的發展,Entity層設計的重要性已經達到和圖像聲音的同等水平,而且已經出現了多種通用型Entity架構。當然,這里伴隨著爭議和分歧。

            直接模式(The C Approach

            這是最早、最簡單、也是任何人都能直接想到的模式。這種方式下一個Entity就是一個簡單的struct:

            struct Mob
            {
            int level, hp, mp, attack, …;
            };

            這種情況下往往需要對不同類型的Entity定義不同的struct,比如PlayerMobItemDoodad等。Entity的數據庫可以直接使用現成的數據庫系統或者從數據文件讀取,比如csv文件。一般會選擇Excel編輯數據庫,然后導出csvExcel的強大表格和統計計算功能是調節游戲數據平衡的得力工具。以致這種最古老而簡單的Entity模式以強大的生命力一直活到現在。

            那么為什么Developers會要去探索其他方式呢?最大的問題是這種方式下各種Entity完全不同質。比如PlayerMobDoodad都有位置,都要做碰撞檢測,但他們卻是不同的類型,不能使用同一份代碼;PlayerMob都有hpmp,也有基本相同的處理邏輯……當然,這個可以用hack的方法解決:只要各個struct前若干個成員類型和順序完全相同,就可以將指針cast成統一的一個Entity類型來處理。不過,任何人都能想到更好的辦法,用類!

            繼承模式(Inheritage

            這也是EntityGameObject等這些名字的由來,他們就是基類。于是我們可以得到一顆繼承關系樹,例如:

            Entity

                   Character

                          Player

                          Mob

                   Missile

                          Laser

                          GuidedMissile

                   Item

                  

            Entity對象的邏輯大多也是直接包含在類里的。但也有人采用了數據對象和邏輯對象的分離,其好處是對相同的數據可以替換不同的邏輯。不過,個人比較懷疑這種分離是否值得,因為類的派生本身就是可以擁有不同的邏輯。

            另外,Entity間的交互除了用標準的直接訪問方式外,經常使用消息模式。消息模式和Windows的消息模式類似,Entity間通過消息交互。雖說窗口編程畫了多年時間才擺脫了當年肥大的switch的消息處理模式,在Entity層使用消息模式還是有意義的,因為消息很容易廣播且可以直接在網絡上發送。執著于OO的人會將消息寫成Command對象,但原理仍然是一樣的。

            同時,聯網游戲出現,指針不能在不同的機器上標識對象,我們必須用穩定的ID來標識對象,于是我們有了EntityManager來分配ID和管理對象集合,或者叫GameObjectManagerObjectManager等。這在一段時期內幾乎成了完美的方案。

            隨著游戲內容的豐富性的迅速膨脹,傳統的由程序員來實現游戲邏輯功能的模式也越來越力不從心。腳本語言的集成將大部分創意性工作從程序員的擔子上拿了下來,交還給游戲設計人員。為了給腳本提供足夠的控制權,Entity結構上必須有充分的靈活性。

            數據驅動(Data-Driven

            現在有句很流行的話,“唯一不變的是變化。(The only constant is change.)”數據驅動使得變化對引擎的影響最小化。數據驅動不能算是一種獨立的Entity模式,不如說它是一種設計思想,其目的就是將內容制作和游戲引擎的制作分離開來。與上面所說的填充Entity屬性數據庫的不同之處在于,它還要能通過數據來設計游戲邏輯。

            游戲設計人員需要的一項最基本功能就是自定義人物屬性,所以與其在類里寫死屬性成員,不如使用屬性表(Attributes/Properties)。通常使用一個哈希表(Hashtable),或者std::map,或者Dictionary,或者就是個數組,隨個人喜好,其實就是要一個key-value的映射表。然后為腳本和編輯器提供對屬性表的操作。

            動態的邏輯主要就靠腳本了,必須為腳本提供足夠的事件和方法。個人推薦用Lua腳本,因為它是在游戲領域內用得最多的通用腳本語言,其引擎很小、速度很快、易于集成,盡管語法過于松散。不要迷信宣傳去用龐大、極慢、難于集成的Python。為腳本提供事件,其實也就是調用腳本里的函數,這里如果使用了前面所述的消息模式,那么就只要調用一個腳本方法,傳遞不同的消息參數就行了。當然也有人覺得這樣很丑陋而更愿意為不同的事件注冊不同的函數。

            當有了數據驅動后,Entity的繼承樹就基本失去意義了,因為一個Entity是什么已經不是程序里決定的了,而是通過數據和腳本設計出來的。但數據和腳本又不是全部,一個Entity的核心內容和需要高效處理的部分,如碰撞檢測,還是要程序來完成。于是我們需要重新設計Entity類,困難的局面也就由此開始。

            一個直觀的想法是一個統一且唯一的Entity類,它包含了所有的基本功能,如顯示、碰撞檢測、運動、背包等,然后由數據決定哪些組件被啟用。比如一個玩家角色可能會啟用絕大部分組件,而一顆樹只啟用顯示和碰撞檢測組件。但也伴隨著缺點:一、這個類太大了;二、對于樹木等一些簡單的Entity也要浪費其他組件的私有數據所占的內存。那么一個簡單的折中是部分使用繼承、部分使用數據定制。例如Entity只提供最基本的組件,再派生出CharactorEntity提供足夠人物角色使用的組件。

            組件模式(Component-Based Entity

            提到組件,那么很自然的就過渡到組件模式,就是把顯示、運動、攻擊、背包、隊伍、聲音等基本功能都做成獨立的組件,由數據來決定向Entity里添加哪些組件。由此可以得到另外一個擴展,就是既然可以有引擎內置的組件,那就也可以有腳本制作的組件,實現腳本模塊的復用。這種模式在GDC2002正式提出,到現在主流的引擎都有這種設計。

            這種模式在理論上很完美,但實踐上還是有不少疑問。最常見的問題就是組件間的依賴關系。理想情況下,各個組件是完全獨立的,但實踐中必然有所依賴。比如運動速度、攻擊強度等和角色的基本屬性有關,運動組件需要角色的包圍盒來測試是否碰撞,AI組件需要分析角色當前狀態和發出運動、攻擊命令,角色動作狀態變化時改變顯示組件屬性,攻擊組件需要訪問隊伍信息組件以禁止攻擊隊友等等。處理這種依賴關系主要要解決兩個問題:

            <!--[if !supportLists]-->一、              <!--[endif]-->誰依賴誰。比如是敏捷屬性改變而去修改移動速度,還是運動組件讀取敏捷屬性來計算移動速度。如果要游戲設計人員自由定義基本屬性的話,就要選擇前者,因為基本屬性組件會是腳本組件。

            <!--[if !supportLists]-->二、              <!--[endif]-->如何取得另一組件的指針/引用。常見的方法是給每個組件類型一個唯一ID,然后用該IDEntity上查詢注冊了的組件,如果找到返回其指針/引用,否則返回null。當然,每次訪問都做這個查詢會很浪費CPU,如果Entity的組件不會在運行時動態添加刪除的話(除非在編輯器里,否則很少有人會這么做),可以在Entity初始化后讓每個組件緩存它所要用的其他組件指針。那么當所依賴的組件不存在怎么辦,一般情況下都是無聲地忽略。

            Entity由很多組件組成后,交互的消息需要發給每一個組件。這里又一次體現出消息機制的優勢,你不需要在Entity里為每一個事件函數寫一個loop來調用組件的相應事件函數。但這里也出現了一個問題,消息到達各個組件的順序。很多時候這個順序不會有什么影響,但個別時候不同的順序會導致完全不同的邏輯發展方向。

            此外,Entity的序列化存儲也變得比較復雜,經典的Excel導出csv的模式難以奏效,因為這里需要結構化的存儲,所以需要結構化的數據文件如XML來存儲,或者完全用腳本來包含所有數據和構造Entity

            據個人經驗,使用數據驅動的繼承模式時很是向往組件模式,感覺上它一個非常自然的擴展方向,但顧忌其引入的額外的復雜性,尤其是需要游戲設計人員具有一定的編程能力,所以一直不敢全盤接過使用。但退一步,在引擎里仍然使用組件思想,但Entity的組件構成在編譯時固定,可以達到某種妥協,這和采用繼承與數據驅動的折中類似。

            混入模式(Mix-in

            這是又一種常見的折中模式,即使用C++的多重繼承,將各個組件類混入一個Entity類。如:

            class Mob: public GameObject, public Renderable, public Movable, public Attackable
            {

            }

            這種方法因其簡單而且非常符合多重繼承設計思想而被很多引擎采用。當然缺點也是只能在支持多重繼承的語言里使用,而且當組件很豐富時,dynamic_cast就變成一個代價高昂的操作。

            功能性與復雜性(Functionality vs Complexity

            編程領域最古老的原則之一就是要“簡單快速”。但隨著問題的復雜化,程序也隨之變得越來越復雜。好的方法應該能有效的降低或隱藏復雜性。但是,沒有不帶副作用的藥(No silver bullet.),在取得更強大的功能的同時總會帶來額外的復雜性。我們需要做出權衡,在必要時犧牲一些功能,也就是要估算性價比。

            一般游戲內容制作人員不會或不太會編程,編程人員也不善于游戲的內容創造和數值平衡,過于復雜的系統會導致需要兩面兼顧的人才,會大大增加做出一款游戲的難度。

            posted @ 2009-08-18 03:11 RedLight 閱讀(1057) | 評論 (0)編輯 收藏

            游戲對象的實現

            狹義的游戲對象是指游戲世界中所能看到及可交互的對象,如玩家、怪物、物品等,我們這里也主要討論這類對象在服務器上的組織及實現。
              

              在大部分的MMOG中,游戲對象的類型都大同小異,主要有物品、生物、玩家等。比如在wow中,通過服務器發下來的GUID我們可以了解到,游戲中有9大類對象,包括物品(Item)、背包(Container)、生物(Unit)、玩家(Player)、游戲對象(GameObject)、動態對象(DynamicObject)、尸體(Corpse)等。

              在mangos的實現中,對象使用類繼承的方式,由Object基類定義游戲對象的公有接口及屬性,包括GUID的生成及管理、構造及更新UpdateData數據的虛接口、設置及獲取對象屬性集的方法等。然后分出了兩類派生對象,一是Item,另一是WorldObject。Item即物品對象,WorldObject顧名思義,為世界對象,即可添加到游戲世界場景中的對象,該對象類型定義了純虛接口,也就是不可被實例化,主要是在Object對象的基礎上又添加了坐標設置或獲取的相關接口。

              Item類型又派兵出了一類Bag對象,這是一種特殊的物品對象,其本身具有物品的所有屬性及方法,但又可作為新的容器類型,并具有自己特有的屬性和方法,所以實現上采用了派生。mangos在實現時對Bag的類型定義做了點小技巧,Item的類型為2,Bag的類型為6,這樣在通過位的方式來表示類型時,Bag類型也就同時屬于Item類型了。雖然只是很小的一個技巧,但在很多地方卻帶來了極大的便利。

              從WorldObject派生出的類型就有好幾種了,Unit、GameObject、DynamicObject和Corpse。Unit為所有生物類型的基類,同WorldObject一樣,也不可被實例化。它定義了生物類型的公有屬性,如種族、職業、性別、生命、魔法等,另外還提供了相關的一些操作接口。游戲中實際的生物對象類型為Creature,從Unit派生,另外還有一類派生對象Player為玩家對象。Player與Creature在實現上最大的區別是玩家的操作由客戶端發來的消息驅動,而Creature的控制是由自己定義的AI對象來驅動,另外Player內部還包括了很多的邏輯系統實現。

              另外還有兩類特殊的Creature,Pet和Totem,其對象類型仍然還是生物類,只是實現上與會有些特殊的東西需要處理,所以在mangos中將其作為獨立的派生類,只是實現上的一點處理。另外在GameObject中也實現有派生對象,最終的繼承關系圖比較簡單,就不麻煩地去畫圖了。

              從我所了解的早期游戲實現來看,大部分的游戲對象結構都是采用的類似這種方式。可能與早期對面向對象的理解有關,當面向對象的概念剛出來時,大家認為繼承就是面向對象的全部,所以處處皆對象,處處皆繼承。

              類實現的是一種封裝,雖然從云風那里出來的棄C++而轉投C的聲音可能會影響一部分人,但是,使用什么語言本身就是個人喜好及團隊整體情況決定的。我們所要的也是最終的實現結果,至于中間的步驟,完全看個人。還是用云風的話說,這只是一種信仰問題,我依然采用我所熟悉的C++,下面的描述也是如此。

              隨著面向對象技術的深入,以及泛型等概念的相繼提出,軟件程序結構方面的趨勢也有了很大改變。C++大師們常說的話中有一句是這樣說的,盡是采用組合而不是繼承。游戲對象的實現也有類似的轉變,趨向于以組合的方式來實現游戲對象類型,也就是實現一個通用的entity類型,然后以腳本定義的方式組合出不同的實際游戲對象類型。

              描述的有些抽象,具體實現下一篇來仔細探討下。


            在游戲編程精粹四有三篇文章講到了實體以及實體管理的實現方案,其中一篇文章說到了實體管理系統的四大要素:定義實體怎樣溝通的實體消息,實現一實體類代碼和數據的實體代碼,維護已經注冊在案的實體類列表,和用來創建、管理、發送消息的實體管理器。

              關于實體消息的內容之前討論事件機制的時候做過一點說明,其實這也就是按接口調用和按消息驅動的區別,現在mangos的做法是完全的接口調用,所以引擎內部就沒有任何的實體消息。實體代碼實現和實體管理器是我們重點要討論的內容。

              另有一篇文章也提到了使用類繼續的方式實現游戲對象的兩大問題,一是它要求系統中的所有對象都必須從一個起點衍生而成,也就是說所有對象類在編譯的時候已經確定,這可能是一個不受歡迎的限制,如果開發者決定添加新的對象類,則必須要對基類有所了解,方能支持新類。另一個問題在于所有的對象類都必須實現同樣的一些底層函數。

              對于第二個問題,可以通過接口繼承的方式來避免基類的方法太多。在mangos的實現中就采用了類似的方法,從Object虛基類派生的Unit和WorldObject仍然還是不可實例化的類,這兩種對象定義了不同的屬性和方法,分來實現不同類型的對象。在游戲內部可以根據對象的實際類型來Object指針向下轉型為Unit或WorldObject,以調用需要的接口。方法雖然不夠OO,也還能解決問題。但是第一個問題是始終無法避免的。

              所以我們便有了通用實體這么一個概念,其主要方法是將原來基類的接口進行分類,分到一個個不同的子類中,然后以對象組合的方式來生成我們所需要的實際游戲對象類型。這個組合的過程可以通過腳本定義的方式,這樣便可以在運行時生成為同的對象類型,也就解決了上面提到的第一個問題。

              通用實體的實現方法在目前的游戲引擎及開源代碼中也可以看到影子。一個是BigWorld,從提供的資料來看,其引擎只提供了一個entity游戲對象,然后由游戲內容實現者通過xml和python腳本來自由定義不同類型的entity類型,每種類型可有不同的property和不同的方法。這樣原來由基類定義的接口完全轉移到腳本定義,具有非常強的靈活性。

              另外還有一個是CEL中的entity實現。按照CEL的描述,entity可以是游戲中的任意對象,包括玩家可交互的對象,如鑰匙、武器等,也可以包括不能直接交互的對象,如游戲世界,甚至任務鏈中的一部分等。entity本身并沒有任何特性,具體的功能實現需要靠附加property來完成。簡單來說,property才定義了entity可以做什么,至于該怎么做,那又是依靠behavior來定義。所以,最終在CEL中一個游戲對象其實是由entity組合了多個property及多個behavior而生成的。

              但是CEL中的property與BigWorld中的property意義不大一樣,在CEL中可定義的property其實是引擎內部要先提供的,比如其示例中所舉的pcobject.mesh、pcmove.linear、pctools.inventory等,而BigWorld中的property是完全的自由定制。從這個角度來講,其實可以把CEL中的property看作是游戲的邏輯系統,也就是我們上面所描述的,接口分類后所定義的子類。

              由引擎內部提供可選擇的property與BigWorld所采用的完全自由定制property其實本質上是相同的。在用BigWorld實現的游戲中,也不可能為每種游戲對象類型都完全從頭定義property,基于代碼利用的原則,也會先定義一些小類,然后在entity類型定義時以自定義property的方式來包含這些小類。當然,我沒有使用過BigWorld,上面的描述也只是基于游戲實現的大原則所做出來的。

              描述的依然有些抽象,我們可以用wow及mangos代碼來說明一下。mangos中為object定義了一個屬性集合,根據對象類型的不同,這個屬性集的大小及保存數據也會有差異,另外游戲對象內部含有處理不同游戲邏輯的系統,如RestSystem、FloodFilterSystem、VariousSystem等等,在player.h中以接口組的方式進行定義。

              如果要將這種結構改為我們描述的通用entity系統,可以讓object只提供property注冊和刪除的接口,這里的property定義與CEL中的相同,放在mangos中也就是上面說的RestSystem、FloodFilterSystem、VariousSystem這些。然后也通過xml文件的方式定義我們所需要的游戲對象類型,如player,creature,item等,不同的對象類型可以選擇加載不同的property,加載的原則是需要哪些功能就加載哪些property。

              對象的定義按上面的方法完成后,對象的實現也需要做一些修改。以前客戶端的消息是直接交由player來處理,AI也是直接調用creature的接口來完成一些功能,現在通用的entity內部已經沒有任何可用的方法,所有的實現都轉到了property中,所以需要由各個property實現自己來注冊感興趣的事件及消息,entity實現一個消息的轉發,轉給對此感興趣的property來處理。其余的實現就沒有什么不同了。

              當然,我們再做一點擴展,讓property不光由引擎來提供,用腳本本身也能定義property,并且可以通過xml來注冊這些property,這樣便實現了與BigWorld一樣的完全自由特性。這其實也就是將很多用C++實現的功能轉移到了python中,這種做法可作為參考,但不一定對所有人合適,至少在我看來,這樣的實現也基本只能由程序員來做,所以讓程序員選擇自己最擅長的語言可能會更易于開發和調試。

            有關游戲對象實現的描述,前面兩篇文章中說的不甚清楚,主要是一直都要引用網上能夠找到的資料來進行描述,以避免與公司引起不必要的麻煩。所以語言有些拼湊的感覺,舉的例子也很不恰當,今天正好看到了游戲編程精粹五和六上的兩篇文章,內容都差不多,<<基于組件的對象管理>>和<<基于組件的游戲對象系統>>,說的也是我上兩篇文章想要描述的內容,所以再補一篇,引用其中的部分文字進行明確的說明。

             

              傳統的游戲對象管理系統采用繼承的方式來實現,例如,所有的子類都從CObject派生。大多數情況下,直接派生的也是抽象類,其中帶一些功能而另一些子類則不帶這些功能,比如可控制/不可控制,可動畫/不可動畫等。mangos的實現中基本就是這種情況,從Object直接派生的Unit和WorldObject都是不可直接實例化的類。

              傳統方法的問題在于無法應對需求的變化,如要求武器也有動畫效果時就無法處理了。如果硬要是這樣做,那隨著需求的嗇,很多的方法會被放到基類中,最終的結果是繼承樹變得越來越頭重腳輕,這樣的類會喪失它的內聚性,因為它們試圖為所有對象完成所有的事。

              就是說到最后,基類會有一個很長的接口列表,而很多的游戲對象類型根本不需要實現其中的一些甚至大部分接口,但是按照這種結構卻又必須去實現。以至于于實現一個非常龐大的對象,而且想要修改一點功能會導致系統的大調整。

              我們希望的系統是可以將現有的功能組合到新的對象中,并且在將新的功能添加到現有的對象中時不需要重構大量的代碼和調整繼承樹的結構。

              實現的方法就是從組件來創建一個對象。組件是一個包含所有相關數據成員和方法的類,它完成某個特定的任務。把幾個組件組合在一起就可以創建一個新的對象。如把Entity組件、Render組件和Collectable組件組合在一起生成了一個Spoon對象。Entity組件讓我們可以把對象放到游戲世界中,Render組件讓我們可以為對象指定一個模型進行渲染,而Collectable組件讓我們可以拾取這個對象。

              關于組件的實現,所有的組件都從一個基礎組件接口派生,可稱其為IComponent。每個組件也有自己的接口定義,并且這個接口也需要從IComponent派生,類似于這樣:IComponent -- ICmpRender -- CCmpRender

              這里的每個組件也就是我在上一篇中所說的由引擎提供的屬性,或者說在BigWorld中自己實現然后定義的屬性,或者使用mangos中的定義,就是一個個的System,雖然mangos并沒有將其完全做成組件,但是通過其代碼注釋可以看到,接口也是按功能組進行了分類,如果要拆分成組件也是比較方便的。

              組件之間的通信有兩種方法,一是用組件ID查詢到組件接口指針,然后調用接口方法;二是使用消息的方式,向對象中所有組件發消息。在初始化的時候,每一個組件類型都會告訴對象管理器應該接收什么樣的消息。

              查詢接口的方法也就是直接的方法調用,只不過接口不是全部在基類中,所以必須先查詢到指定的組件然后才能調用其接口。消息的使用前面已經說過多次,其實現方案也有過說明。

              最后是關于游戲對象功能的擴展和游戲對象的定義。需要擴展功能也就是需要實現一個新的組件,或者修改現在組件。在大多數情況下,擴展都不會引起結構的很大調整,受影響的最多只是使用到該組件的部分代碼。

              游戲對象定義可采用完全數據驅動的方式,使用xml或者腳本語言來定義對象類型,以及每個類型需要加載的組件。對象類型注冊到對象管理器后,由管理器提供創建指定類型的對象的方法。數據驅動的方式能夠讓策劃自由定義游戲對象類型,并且隨時可自由創建新的對象類型。


            posted @ 2009-07-29 14:04 RedLight 閱讀(2439) | 評論 (1)編輯 收藏

            虛擬貨幣可能引發現實風險 金融部門緊急應對

             

            adsense
            中國高速發展的互聯網市場讓無數的游戲廠商看到這是一個即將到來的金蛋。

              據中國互聯網絡信息中心(CNNIC)最近的一次《中國互聯網絡發展狀況統計報告》顯示,中國網民人數達到了1.23億人,與去年同期相比增長了19.4%,調查結果表明,人們對互聯網的使用越來越頻繁,本次調查網民平均每周上網16.5小時,達到了新的歷史高度,這一數據已經超過了許多互聯網發達國家和地區的網民平均上網時長。

              總體而言,中國互聯網正處于一個新的高速增長時期,與GDP增長率相近的印度相比,中國的互聯網普及率高了一倍。但在這些喜人成績背后卻無法掩飾中國互聯網經濟尤其是虛擬貨幣帶來的不合理問題。

              網絡催生虛擬貨幣

              不知不覺之中,伴隨著中國互聯網成長起來的還有大量的網絡游戲廠商等。隨著互聯網的發展壯大,在給廣大網民提供大量免費服務的同時,根據公司盈利需要和用戶多樣化需求,各個網站紛紛推出了收費服務項目。這也推動了虛擬貨幣的產生。

              不少門戶網站、網絡游戲運營商為了提供更好的服務,很早就開始提供虛擬貨幣以供使用。據不完全統計,目前市面流通的網絡虛擬貨幣(簡稱網幣)不下10種,如Q幣、百度幣、酷幣、魔獸幣、天堂幣、盛大(游戲區)點券等。以Q幣為例,使用者超過兩億人。業內人士估計,國內互聯網已具備每年幾十億元的虛擬貨幣市場規模,并以15%~20%的速度成長。

              在市場經濟中,需求會刺激創新,創新反過來能拉動需求。虛擬貨幣的日漸火爆正是在用戶的需求和企業的創新中完成的。目前中國網絡市場的虛擬貨幣種類之多,不勝枚舉,其中騰訊公司依托龐大的QQ用戶,隨著即時通信市場的成熟,也適時推出了Q幣。

              不可否認的是,虛擬貨幣讓我們感受到購買網絡服務的便利性,這是網絡經濟發展的必然產物。但目前網絡虛擬貨幣已經悄然變身,形成龐大的交易市場。

              美國著名經濟學家林頓·拉魯什也曾經預言:從2050年開始,網絡的虛擬貨幣將在某種程度上得到官方承認,成為可以流動的通行貨幣。如此規模的市場和光明前景,使不少公司已經躍躍欲試,意圖占領這塊市場。現在看來,百度、PAYPAL(貝寶)和騰訊處于領先地位。

              虛擬的貨幣體系

              可以說,如今的網絡貨幣已經成為網絡上最受人們關注的一種虛擬經濟現象。

              不少虛擬貨幣都有一整套完整的虛擬貨幣銷售體系。就拿Q幣來說,已經有了一個非常大的貨幣銷售體系,可以進行聲訊電話充值、可以通過人民幣直接到代理商處購買、也可以用手機話費進行購買。

              毋庸置疑,在網游世界中,騰訊公司的Q幣已經承擔了許多超出其規定功用的職能,比如,在虛擬交易中充當一般等價物,成為虛擬財富與現實財富之間的橋梁。

              Q幣之所以成為虛擬世界的硬通貨,這當然首先應歸功于Q幣的“硬”,與諸多游戲幣相比:它歷時更長,使用人數眾多,使用范圍也比較廣,不易貶值……以至于在此之上,網民們又賦予了它新的功用,成為虛擬世界的“美元”,被用來交易不同虛擬世界以及虛擬世界和現實世界之間的虛擬財富。反過來,Q幣這種網絡一般等價物的角色又強化了其早已擁有的那些優勢。

              Q幣之所以有如此“強大”的功能,龐大的用戶群則是其天然的基礎。據不完全統計,2005年中國即時通訊軟件的最高同時在線人數已經突破千萬。在用戶最常用的即時通訊軟件中,騰訊公司的QQ用戶占據了絕對的市場份額。截止到目前為止,QQ用戶已經突破4億用戶。

              Q幣能夠成為虛擬世界的硬通貨,對于騰訊公司來說,不但是一種榮譽,更意味著經濟利益的滋生。業內人士分

              析,虛擬交易商在相當程度上促進了游?分行檳餼玫姆⒄寡貳?

              根據騰訊公司2006年第二季度財報的數據,其互聯網增值服務收入為4.62億元,占總收入近66%,易觀國際分析認為,騰訊互聯網業務很大程度上依賴于虛擬貨幣Q幣來運行。

              淘寶網9月的統計數據顯示,前不久超女投票由短信改為Q幣投票后,專門從事騰訊產品銷售的賣家已經突破3000家,商品數量已經超過30萬,QQ專區每天的交易額都超過55萬元。

              事實上,歐美國家2005年虛擬交易成交超過42億美元,在網絡游戲的“圣地”———韓國,虛擬交易的交易額在幾年前就已經超過了網絡游戲運營商的收入。

              虛擬貨幣的社會體系

              盡管網絡虛擬貨幣大大激發了網絡使用者的積極性,但也表現出了一定的社會危害性。

              這類網絡貨幣的危險性也即在于虛擬性,理論上它可以被無限生成而不像貨幣需要黃金來支撐。當網上存在的貨幣達到一定程度時,必然引起網上虛擬的通貨膨脹。

              國家工商部門有關人員表示,虛擬貨幣交易是網絡時代派生的經營行為,至今還沒有明確的法律條文規范,工商注冊范圍也沒有關于虛擬物品交易的項目。但當虛擬財產交易逐漸人員雇傭化、場所固定化、交易盈利明確化,具有經營性質后,就可能涉及市場秩序、稅收問題。

              更有甚者,不少網絡游戲廠商都選擇了網絡賭博這樣灰色的產品作支柱。

              網游公司在大量發行虛擬貨幣并從中牟利時,很容易造成玩家花錢買來的虛擬財產貶值,利益受到損害的現象。這是因為在互聯網上,虛擬貨幣如騰訊Q幣或其他虛擬貨幣,它們的發行和金屬貨幣儲值沒有任何關系,虛擬貨幣發行商的發幣行為也不受任何部門的監管。

              甚至,游戲運營商也沒有能力來控制,網絡中存在大量“偽鈔制造者”,以主營棋牌類網游的邊鋒為例,其網幣對應的購買力曾在一年內縮水近40%。不少人士指出:這樣的通貨膨脹只會讓網民受損,也會讓網民喪失對互聯網的信心。

              據悉,正常情況下,現今使用最為廣泛的Q幣只能實現人民幣→Q幣→游戲幣→增值服務的單向流通,即在正常渠道中用人民幣兌換成Q幣之后,不可以再將Q幣兌換成人民幣。

              包括騰訊在內的提供網絡虛擬貨幣的游戲運營商等都表示,提供產品和服

              務不允許將其虛擬貨幣轉換為人民幣。但是在騰訊旗下的拍拍網首頁就有非常明顯的“騰訊QQ專區”標志,騰訊客服小姐明確告知,設定類目、制作專題、推薦商品都屬于網站官方權限,只有拍拍網官方才能進行相關操作。

              盡管目前國家對虛擬貨幣也沒有明確的監管辦法,但是在有關的基本法律法規里明確了其他形式的代金券等,不能與人民幣進行反向兌換,這就等于明確了不允許Q幣這樣的“虛擬貨幣”兌換成人民幣。但是現實中將Q幣兌換成人民幣并不鮮見,還出現了專門銷售這種“虛擬貨幣”的網站,甚至都出現了專門的網上“倒爺”,這些“倒爺”們的活動表現為低價收購各種虛擬貨幣、虛擬產品,然后再高價賣出,在一定程度上實現了虛擬貨幣與人民幣之間的雙向流通,對外經貿大學信息學院院長陳進表示:“虛擬貨幣但凡跟人民幣發生聯系,就會跟現實中的銀行一樣,可能面對擠兌等現實風險。”

              業內人士認為,如果要控制上述情況的發展,最根本的就是要出臺措施,切斷Q幣等“虛擬貨幣”兌人民幣的途徑,比如主管部門可以對那些專門從事虛擬貨幣兌換的網站進行監管。

              可喜的是,目前中國人民銀行辦公廳主任、新聞發言人李超表示,央行已開始關注虛擬貨幣的發展,目前正在認真研究。

              網絡交易的發展,需要虛擬貨幣,虛擬貨幣的發展是網絡環境下不可阻擋的趨勢。但如何讓虛擬貨幣扮演好在網絡交易中的角色,無時不在考驗著網絡服務商和中國金融部門的智慧。

            posted @ 2009-06-05 02:19 RedLight 閱讀(322) | 評論 (0)編輯 收藏

            學3D虛擬現實技術有感

            最近想研究一下虛擬現實(VR)方面的編輯器,  因為我做過游戲地圖編輯器, 所以比較好奇, 都是調用3D渲染和寫3D Max插件, 但畢竟兩種編輯器所偏向領域不同, 不同專業有些東西是可以互補的。

            初次入門,我選擇了上海一家聽說比較著名的公司《中視典》,聽說他們公司的產品很經典,已是統治位置了,所以我就下載了個共享版的VRP編輯器來試用一下,同時下載了官方的視頻教程及使用幫助文檔,還有下載了一個我以后夢想中的《二層別墅.exe》來Look 1 Look,呵呵!





            感覺VR地圖編輯器想得還是很到位的,還有視頻教程幾百M, 相當精彩,講得很細,包含在使用3D Max插件出現的問題也講了,還有3D模型優化方面的知識,受益非淺啊,特別是美術那一塊的東西,其中比較吸引我的是攝相機實時反射貼圖,烘培,還有玻璃材質等。我終于知道室內場景的一些美術知識了,不限于無邊無際在游戲野外打怪那么姑燥無味!

            posted @ 2009-05-29 15:43 RedLight 閱讀(608) | 評論 (1)編輯 收藏

            僅列出標題
            共9頁: 1 2 3 4 5 6 7 8 9 
            <2009年12月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            導航

            統計

            公告


            Name: Galen
            QQ: 88104725

            常用鏈接

            留言簿(3)

            隨筆分類

            隨筆檔案

            相冊

            My Friend

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            久久天天躁狠狠躁夜夜躁2O2O| 久久久久国产精品嫩草影院| 久久久中文字幕日本| 国产综合免费精品久久久| 国产精品亚洲美女久久久| 精品国产青草久久久久福利 | 久久天天躁狠狠躁夜夜不卡 | 精品久久久久久久中文字幕| 久久国产亚洲精品麻豆| 丁香久久婷婷国产午夜视频| 国产99久久久国产精品~~牛| 久久久久久曰本AV免费免费| 日韩精品久久无码人妻中文字幕| 91精品国产色综久久| 很黄很污的网站久久mimi色| 久久久亚洲欧洲日产国码aⅴ | 狠狠色丁香婷婷综合久久来来去| 伊人 久久 精品| 婷婷五月深深久久精品| 香蕉aa三级久久毛片| 色妞色综合久久夜夜| 99久久99久久精品国产| 亚洲精品无码久久久| 久久er国产精品免费观看2| 久久WWW免费人成—看片| 国产成人久久激情91| 日韩久久久久中文字幕人妻| 久久狠狠高潮亚洲精品| 久久性生大片免费观看性| 久久99精品久久久久久动态图| 国产亚洲精品久久久久秋霞 | 国产亚洲精午夜久久久久久| 亚洲国产小视频精品久久久三级 | 99久久免费国产特黄| 国产69精品久久久久9999APGF | 久久电影网一区| 久久露脸国产精品| 精品国产乱码久久久久久浪潮| 狠狠色婷婷综合天天久久丁香| 中文字幕久久精品无码| 久久国产午夜精品一区二区三区|