讨论如何用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 algorithm int 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 algorithm int 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 line lineVertices[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 line lineVertices[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 line lineVertices[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结构的内存。