DirectX画圆及圆角矩形的简单实现

    讨论如何用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结构的内存。

你可能感兴趣的:(C++,Win32,DirectX)