討論如何用d3d9來繪製圓及簡單的圓角矩形。
畫圓時採用Bresenham演算法。不失一般性,假設圓的圓心位於座標原點(如果圓心不在原點,可以通過座標平移使其與原點重合),半徑為R。以原點為圓心的圓C有四條對稱軸:x=0,y=0,x=y和x=-y。若已知圓弧上一點P1=C(x, y),利用其對稱性便可以得到關於四條對稱軸的其它7個點,即:
P2=C(x,-y),
P3=C(-x, y),
P4=C(-x,-y),
P5=C(y,x),
P6=C(-y,x),
P7=C(y,-x),
P8=C(-y,-x)。
這種性質稱為八對稱性。因此,只要掃描轉換八分之一圓弧,就可以通過圓弧的八對稱性得到整個圓。
我們以(0,0)為原點,r為半徑,座標系xy方向與螢幕座標系一致,計算y軸正向右側的八分之一圓弧,其它圓弧通過對稱性得到。
頂點格式採用如下定義:
struct SCREEN_VERTEX_UNTEX {float x, y, z, h;D3DCOLOR color;static DWORD FVF; }; SCREEN_VERTEX_UNTEX::FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE
下面是畫圓函數:
void DrawCircle( IDirect3DDevice9* pd3dDevice, int xCenter, int yCenter, int nRadius, D3DCOLOR FrameColor){SCREEN_VERTEX_UNTEX *pVertices = new SCREEN_VERTEX_UNTEX[2 * D3DX_PI * nRadius];//Bresenham algorithmint x=0, y=nRadius, d=1-nRadius, i=0;while(x <= y){//get eight points//(x,y)pVertices[i].x = x + xCenter;pVertices[i].y = y + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(x,-y)++i;pVertices[i].x = x + xCenter;pVertices[i].y = -y + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(-x, y)++i;pVertices[i].x = -x + xCenter;pVertices[i].y = y + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(-x, -y)++i;pVertices[i].x = -x + xCenter;pVertices[i].y = -y + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(y, x)++i;pVertices[i].x = y + xCenter;pVertices[i].y = x + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(-y, x)++i;pVertices[i].x = -y + xCenter;pVertices[i].y = x + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(y, -x)++i;pVertices[i].x = y + xCenter;pVertices[i].y = -x + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(-y,-x)++i;pVertices[i].x = -y + xCenter;pVertices[i].y = -x + yCenter;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;++i;if(d>0){d+=2*(x-y)+5;--y;}else{d+=2*x+3;}++x;}pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_SEPARATEALPHABLENDENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA|D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_RED );pd3dDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );pd3dDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE);pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );pd3dDevice->SetFVF(SCREEN_VERTEX_UNTEX::FVF);pd3dDevice->DrawPrimitiveUP(D3DPT_POINTLIST, i, pVertices, sizeof(SCREEN_VERTEX_UNTEX));delete [] pVertices;}
圓弧上像素點的個數為2*D3DX_PI*R,通過Bresenham演算法逼近,產生的點的個數不會多於上面計算的點的個數。在得到一個點後,利用對稱性,獲得其它7個點。所有的點先放入頂點緩衝區,最後一次性提交。
畫圓角矩形的方法和畫圓類似,分別畫四個圓弧,然後畫四條線即可。為方便計算,這裡只考慮圓角為四分之一圓弧的情況。
void DrawRoundRect( IDirect3DDevice9 * pd3dDevice, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nRadius, D3DCOLOR FrameColor ){SCREEN_VERTEX_UNTEX *pVertices = new SCREEN_VERTEX_UNTEX[2 * D3DX_PI * nRadius];//Bresenham algorithmint x=0, y=nRadius, d=1-nRadius, i=0;while(x <= y){//get eight points//right bottom//(x,y)pVertices[i].x = x + nRightRect - nRadius;pVertices[i].y = y + nBottomRect - nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(y, x)++i;pVertices[i].x = y + nRightRect - nRadius;pVertices[i].y = x + nBottomRect - nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//right top//(x,-y)++i;pVertices[i].x = x + nRightRect - nRadius;pVertices[i].y = -y + nTopRect + nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(y, -x)++i;pVertices[i].x = y + nRightRect - nRadius;pVertices[i].y = -x + nTopRect + nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//left bottom//(-x, y)++i;pVertices[i].x = -x + nLeftRect + nRadius;pVertices[i].y = y + nBottomRect - nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(-y, x)++i;pVertices[i].x = -y + nLeftRect + nRadius;pVertices[i].y = x + nBottomRect - nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//left top//(-x, -y)++i;pVertices[i].x = -x + nLeftRect + nRadius;pVertices[i].y = -y + nTopRect + nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;//(-y,-x)++i;pVertices[i].x = -y + nLeftRect + nRadius;pVertices[i].y = -x + nTopRect + nRadius;pVertices[i].z = 0.5f;pVertices[i].h = 1.0f;pVertices[i].color = FrameColor;++i;if(d>0){d+=2*(x-y)+5;--y;}else{d+=2*x+3;}++x;}static DXUT_SCREEN_VERTEX_UNTEX lineVertices[8] = {0};//top linelineVertices[0].x = nLeftRect + nRadius;lineVertices[0].y = nTopRect;lineVertices[0].z = 0.5f;lineVertices[0].h = 1.0f;lineVertices[0].color = FrameColor;lineVertices[1].x = nRightRect - nRadius;lineVertices[1].y = nTopRect;lineVertices[1].z = 0.5f;lineVertices[1].h = 1.0f;lineVertices[1].color = FrameColor;//right linelineVertices[2].x = nRightRect;lineVertices[2].y = nTopRect + nRadius;lineVertices[2].z = 0.5f;lineVertices[2].h = 1.0f;lineVertices[2].color = FrameColor;lineVertices[3].x = nRightRect;lineVertices[3].y = nBottomRect - nRadius;lineVertices[3].z = 0.5f;lineVertices[3].h = 1.0f;lineVertices[3].color = FrameColor;//bottom linelineVertices[4].x = nRightRect - nRadius;lineVertices[4].y = nBottomRect;lineVertices[4].z = 0.5f;lineVertices[4].h = 1.0f;lineVertices[4].color = FrameColor;lineVertices[5].x = nLeftRect + nRadius;lineVertices[5].y = nBottomRect;lineVertices[5].z = 0.5f;lineVertices[5].h = 1.0f;lineVertices[5].color = FrameColor;//left line lineVertices[6].x = nLeftRect;lineVertices[6].y = nBottomRect - nRadius;lineVertices[6].z = 0.5f;lineVertices[6].h = 1.0f;lineVertices[6].color = FrameColor;lineVertices[7].x = nLeftRect;lineVertices[7].y = nTopRect + nRadius;lineVertices[7].z = 0.5f;lineVertices[7].h = 1.0f;lineVertices[7].color = FrameColor;pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_SEPARATEALPHABLENDENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA|D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_RED );pd3dDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );pd3dDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE);pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );pd3dDevice->SetFVF(DXUT_SCREEN_VERTEX_UNTEX::FVF);pd3dDevice->DrawPrimitiveUP(D3DPT_POINTLIST, i, pVertices, sizeof(SCREEN_VERTEX_UNTEX));pd3dDevice->DrawPrimitiveUP(D3DPT_LINELIST, 4, lineVertices, sizeof(SCREEN_VERTEX_UNTEX));delete [] pVertices;}
在上面的兩個函數中,每畫一次就new一塊記憶體,繪製完成後釋放。效能不好,可以依據需要,預先申請一塊足夠大的記憶體供使用。加入程式中可能出現的圓的半徑不超過200像素,那麼可以預先分配容納2*D3DX_PI*200個SCREEN_VERTEX_UNTEX結構的記憶體。