讨论如何用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结构的内存。
本文介绍了如何利用DirectX的D3D9接口结合Bresenham算法来绘制圆形和圆角矩形。通过探讨八对称性,详细阐述了如何扫描转换八分之一圆弧,进而生成整个圆。同时,提到了圆角矩形的绘制思路,即绘制四个四分之一圆弧与四条直线。
1559

被折叠的 条评论
为什么被折叠?



