D3D10与Geometry Shader学习笔记(2)

D3D10与Geometry Shader学习笔记(2)

华南理工大学,张嘉华 [email protected] QQ:188318005,欢迎和我多多交流,共同学习进步

1. 贝赛尔曲线在Geometry Shader下的实现

贝赛尔曲线在图形绘制中经常用到,在这一节里面讲介绍如何在Geometry Shader下实现贝赛尔曲线的绘制.首先来看最简单的贝赛尔曲线,就是由四个点控制.

考虑到贝赛尔曲线输入是四个点,而我们这里是为二维图形绘制实时的贝赛尔曲线,因此可以用两个四维的顶点表示这四个二维的点,因为对于Geometry Shader输入的类型中point必须是一个点,line是两个点,triangle是三个点,也就是input数组的大小,而不存在四个元素的输入数组类型.在GS_Bezier()首先把两个四维的点转换为四个二维的点.接着根据贝赛尔曲线的函数,通过100次线段细分来逼近曲线,因此t=1/100,计算t1,t2为处于整段曲线的比例,接着利用贝赛尔曲线函数计算出每个线段的两端的两个点,接着Append到输出流,每输出一段线段都需要RestartStrip()一下,以确认以之前append的数据构成图元输出了.输出流这里定义为LineStream表示线段流.下面是HLSL文件,重点是Geometry Shader的代码:

struct GSIn

{

float4 pos : POSITION;

};

GSIn VS_Bezier(int i : TEXCOORD0)

{

GSIn Output;

Output.pos = pos[i];

return Output;

}

[maxvertexcount(100)]

void GS_Bezier(line GSIn input[2], inout LineStream<PS_INPUT> LineOutputStream)

{

float2 positions[4];

positions[0]=input[0].pos.xy;

positions[1]=input[0].pos.zw;

positions[2]=input[1].pos.xy;

positions[3]=input[1].pos.zw;

for(int i=0;i<=99;i++)

{

float t1=i*0.01;

float x1=positions[0].x*(1-t1)*(1-t1)*(1-t1)+positions[1].x*(1-t1)*(1-t1)*3*t1+positions[2].x*(1-t1)*3*t1*t1+positions[3].x*t1*t1*t1;

float y1=positions[0].y*(1-t1)*(1-t1)*(1-t1)+positions[1].y*(1-t1)*(1-t1)*3*t1+positions[2].y*(1-t1)*3*t1*t1+positions[3].y*t1*t1*t1;

float t2=(i+1)*0.01;

float x2=positions[0].x*(1-t2)*(1-t2)*(1-t2)+positions[1].x*(1-t2)*(1-t2)*3*t2+positions[2].x*(1-t2)*3*t2*t2+positions[3].x*t2*t2*t2;

float y2=positions[0].y*(1-t2)*(1-t2)*(1-t2)+positions[1].y*(1-t2)*(1-t2)*3*t2+positions[2].y*(1-t2)*3*t2*t2+positions[3].y*t2*t2*t2;

PS_INPUT output1;

output1.Pos=float4(x1,y1,0.0f,1.0f);

output1.color=float4(1,1,1,1);

output1.uv=float2(0,0);

LineOutputStream.Append(output1);

PS_INPUT output2;

output2.Pos=float4(x2,y2,0.0f,1.0f);

output2.color=float4(1,1,1,1);

output2.uv=float2(0,0);

LineOutputStream.Append(output2);

LineOutputStream.RestartStrip();

}

}

float4 PS_Bezier(PS_INPUT input) : SV_Target

{

return input.color;

}

technique10 Bezier

{

pass P0

{

SetVertexShader( CompileShader( vs_4_0, VS_Bezier() ) );

SetGeometryShader( CompileShader( gs_4_0, GS_Bezier() ) );

SetPixelShader( CompileShader( ps_4_0, PS_Bezier() ) );

SetBlendState( AdditiveBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );

SetDepthStencilState( DisableDepth, 0 );

}

}

下面是CPP文件,需要在其中调用IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_LINELIST )设置输入IA为线段列类型:

HRESULT G2DGraphics::DrawBezier(float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,DWORD color)

{

HRESULT hr=S_OK;

ID3D10Device* pd3dDevice=GGraphicsDevice::GetDevice();

//透明则略过

if (color>>24==0)

return S_OK;

float fdevvicewidth=(float)(GGraphicsDevice::GetDeviceWidth());

float fdeviceheight=(float)(GGraphicsDevice::GetDeviceHeight());

float4 positions[4];

float4 colors[4];

float4 uvs[4];

positions[0].x=((float)(x1)/fdevvicewidth-0.5f)*2.0f;

positions[0].y=((float)(y1)/fdeviceheight-0.5f)*-2.0f;

positions[0].z=((float)(x2)/fdevvicewidth-0.5f)*2.0f;

positions[0].w=((float)(y2)/fdeviceheight-0.5f)*-2.0f;

colors[0]=ConvertColorFromDwordToFloat4(color);

positions[1].x=((float)(x3)/fdevvicewidth-0.5f)*2.0f;

positions[1].y=((float)(y3)/fdeviceheight-0.5f)*-2.0f;

positions[1].z=((float)(x4)/fdevvicewidth-0.5f)*2.0f;

positions[1].w=((float)(x4)/fdevvicewidth-0.5f)*2.0f;

colors[1]=ConvertColorFromDwordToFloat4(color);

g_VertexPositions->SetFloatVectorArray((float*)&positions,0,4);

g_VertexColors->SetFloatVectorArray((float*)&colors,0,4);

// Set the input layout

pd3dDevice->IASetInputLayout( g_pVertexLayout );

// Set vertex buffer

UINT stride = sizeof( UINT );

UINT offset = 0;

pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_LINELIST );

D3D10_TECHNIQUE_DESC techDesc;

g_pBezierTech->GetDesc( &techDesc );

for( UINT p = 0; p < techDesc.Passes; ++p )

{

g_pBezierTech->GetPassByIndex( p )->Apply(0);

pd3dDevice->Draw( 2, 0 );

}

return S_OK;

}

2. 圆形在Geometry Shader下的实现

圆形在图形绘制中也经常用到,由半径和中心坐标控制,下面是实现的HLSL代码,同样也是通过把圆形细分为特定数量线段进行逼近,根据角度计算圆形上线段的两端顶点.

[maxvertexcount(100)]

void GS_Circle(point GSIn input[1], inout LineStream<PS_INPUT> LineOutputStream)

{

int number=40;

float2 position=input[0].pos.xy;

float radius=input[0].pos.z;

float dangle=3.1415926f*2.0f/number;

for(int i=0;i<number;i++)

{

float t1=i*dangle;

float x1=radius*cos(t1)+position.x;

float y1=radius*sin(t1)+position.y;

float t2=(i+1)*dangle;

float x2=radius*cos(t2)+position.x;

float y2=radius*sin(t2)+position.y;

PS_INPUT output1;

output1.Pos=float4(x1,y1,0.0f,1.0f);

output1.color=float4(1,1,1,1);

output1.uv=float2(0,0);

LineOutputStream.Append(output1);

PS_INPUT output2;

output2.Pos=float4(x2,y2,0.0f,1.0f);

output2.color=float4(1,1,1,1);

output2.uv=float2(0,0);

LineOutputStream.Append(output2);

LineOutputStream.RestartStrip();

}

}

下面是CPP文件,输入类型为D3D10_PRIMITIVE_TOPOLOGY_POINTLIST,因为只需要圆心坐标(x,y)和半径,用一个四维顶点就可以存储了.

HRESULT G2DGraphics::DrawCircle(float x,float y,float r,DWORD color)

{

HRESULT hr=S_OK;

ID3D10Device* pd3dDevice=GGraphicsDevice::GetDevice();

//透明则略过

if (color>>24==0)

return S_OK;

float fdevvicewidth=(float)(GGraphicsDevice::GetDeviceWidth());

float fdeviceheight=(float)(GGraphicsDevice::GetDeviceHeight());

float4 positions[4];

float4 colors[4];

float4 uvs[4];

positions[0].x=((float)(x)/fdevvicewidth-0.5f)*2.0f;

positions[0].y=((float)(y)/fdeviceheight-0.5f)*-2.0f;

positions[0].z=((float)(r)/fdevvicewidth)*2.0f;

colors[0]=ConvertColorFromDwordToFloat4(color);

g_VertexPositions->SetFloatVectorArray((float*)&positions,0,4);

g_VertexColors->SetFloatVectorArray((float*)&colors,0,4);

// Set the input layout

pd3dDevice->IASetInputLayout( g_pVertexLayout );

// Set vertex buffer

UINT stride = sizeof( UINT );

UINT offset = 0;

pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_POINTLIST );

D3D10_TECHNIQUE_DESC techDesc;

g_pCircleTech->GetDesc( &techDesc );

for( UINT p = 0; p < techDesc.Passes; ++p )

{

g_pCircleTech->GetPassByIndex( p )->Apply(0);

pd3dDevice->Draw( 1, 0 );

}

return S_OK;

}

介绍了两种简单的二维图形在Geometry Shader的绘制后,在学习笔记3里将介绍较为复杂的Geometry Shader下的LOD应用.

你可能感兴趣的:(学习笔记)