Directx11:基于GPU_GeometryShader的Billboard公告板绘制

    公告板是游戏里一很常见的技术,用来绘制树木,草地,爆炸效果等。说白了就求一个矩形框,然后贴一张照片上去。(Ps下由于个人博客所以讲的比较随便,有时候讲billboard,有时候公告板,有时候一个矩形框,都指差不多一个东西)

    而Geometry Shader则是Directx10里面推出的一个新的Shader能够处理完整的几何模型。想典型VertexShader只知道并处理顶点,而Geometry Shader知道顶点,直线和三角形并处理它们。

    这里用个Demo阐述下如何用GeomtryShaderGPU中绘制Billboard公告板。

    1先讲下求公告板的算法。说白了就是求它的世界坐标系。

    假设这个公告板上方的向量是v,面向我们眼睛向量w,公告板右手方向u。公告板中心位置为C,我们眼睛位置为E,则有:

    用图表达下场景:

   则该公告板的世界坐标系为

    知道了其算法后,在GeomtryShader中我们主要根据传进来的公告板的中心点位置,求它的世界矩坐标系转换阵。

[maxvertexcount(4)]

void GS(point VS_OUT gIn[1],uint primID:SV_PrimitiveID,inout TriangleStream<GS_OUT> triStream)

{

float halfWidth=0.5f*gIn[0].sizeW.x;

float halfHeight=0.5f*gIn[0].sizeW.y;

float4 v[4];

v[0]=float4(-halfWidth,-halfHeight,0.0f,1.0f);

v[1]=float4(halfWidth,-halfHeight,0.0f,1.0f);

v[2]=float4(-halfWidth,halfHeight,0.0f,1.0f);

v[3]=float4(halfWidth,halfHeight,0.0f,1.0f);

float2 texC[4];

texC[0]=float2(0.0f,1.0f);

texC[1]=float2(1.0f,1.0f);

texC[2]=float2(0.0f,0.0f);

texC[3]=float2(1.0f,0.0f);

float3 up=float3(0.0f,1.0f,0.0f);

float3 look=gEyePosW-gIn[0].posW;

look.y=0.0f;

look=normalize(look);

float3 right=cross(up,look);

matrix world;

world[0]=float4(right,0.0f);

world[1]=float4(up,0.0f);

world[2]=float4(look,0.0f);

world[3]=float4(gIn[0].posW,1.0f);

GS_OUT gOut;

//[unroll]

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

{

gOut.posW=mul(v[i],world);

gOut.posH=mul(v[i],world);

gOut.posH=mul(gOut.posH,View);

gOut.posH=mul(gOut.posH,Projection);

gOut.normalW=look;

gOut.texC=texC[i];

gOut.primID=primID;

triStream.Append(gOut);

}

}

    2了解了公告板的算法以及如何在GeometryShader中实现这个算法之后,因为上面得到的只是一堆矩形框的位置,所以我们需要载入一组纹理图片,方便把这些纹理贴到这些矩形框上去。

    总的来说我们是把几张纹理图片放到一个texture2Darray里。Directx11ID3D11Texture2D 表示这个2d纹理数组(没看错,不管是一张纹理,还是多张,Directx11都是ID3D11Texture2D 来表示)。

    创建步骤:

    1读入图片,为每一张图片创建一个ID3D11Texture2D srcTex[i]。

    2创建用来存储上面所有纹理的纹理数组ID3D11Texture2D texArray;

    3将每个纹理srcTex[i]拷贝到texArray相应的位置。

    4为texArray创建一个shader resource view.

    具体函数如下:

HRESULT Tree::BuildShaderResourceView()

{

HRESULT hr=S_OK;

std::wstring filenames[4]={ L"tree0.dds",L"tree1.dds",L"tree2.dds",L"tree3.dds" };

// 1读入图片,为每一张图片创建一个ID3D11Texture2D srcTex[i]。

ID3D11Texture2D* srcTex[4];

for(UINT i=0;i<4;i++)

{

D3DX11_IMAGE_LOAD_INFO loadInfo;

loadInfo.Width=D3DX11_DEFAULT;

loadInfo.Height=D3DX11_DEFAULT;

loadInfo.Depth=D3DX11_DEFAULT;

loadInfo.CpuAccessFlags=D3D11_CPU_ACCESS_READ;

loadInfo.BindFlags=0;

loadInfo.Filter=D3DX11_FILTER_NONE;

loadInfo.MipFilter=D3DX11_FILTER_NONE;

loadInfo.FirstMipLevel=0;

loadInfo.Format=DXGI_FORMAT_R8G8B8A8_UNORM;

loadInfo.MipLevels=D3DX11_DEFAULT;

loadInfo.MiscFlags=0;

loadInfo.Usage=D3D11_USAGE_STAGING;

loadInfo.pSrcInfo=0;

IFR(D3DX11CreateTextureFromFile(m_pDevice,filenames[i].c_str(),&loadInfo,NULL,(ID3D11Resource**)&srcTex[i],NULL));

}

//2创建用来存储上面所有纹理的纹理数组ID3D11Texture2D texArray;

D3D11_TEXTURE2D_DESC texDesc;

srcTex[0]->GetDesc(&texDesc);

D3D11_TEXTURE2D_DESC texArrayDesc;

texArrayDesc.Width=texDesc.Width;

texArrayDesc.Height=texDesc.Height;

texArrayDesc.MipLevels=texDesc.MipLevels;

texArrayDesc.ArraySize=4;

texArrayDesc.BindFlags=D3D11_BIND_SHADER_RESOURCE;

texArrayDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;

texArrayDesc.CPUAccessFlags=0;

texArrayDesc.SampleDesc.Count=1;

texArrayDesc.SampleDesc.Quality=0;

texArrayDesc.MiscFlags=0;

texArrayDesc.Usage=D3D11_USAGE_DEFAULT;

ID3D11Texture2D *texArray=NULL;

IFR(m_pDevice->CreateTexture2D(&texArrayDesc,NULL,&texArray));

//3将每个纹理srcTex[i]拷贝到texArray相应的位置。

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

{

for(int j=0;j<texDesc.MipLevels;j++)

{

D3D11_MAPPED_SUBRESOURCE subTex;

m_pContext->Map(srcTex[i],j,D3D11_MAP_READ,0,&subTex);

m_pContext->UpdateSubresource(texArray,D3D11CalcSubresource(j,i,texDesc.MipLevels),NULL,subTex.pData,subTex.RowPitch,0);

m_pContext->Unmap(srcTex[i],j);

}

}

//4为texArray创建一个shader resource view.

D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;

ZeroMemory(&srvDesc,sizeof(srvDesc));

srvDesc.Format=texArrayDesc.Format;

srvDesc.Texture2DArray.MipLevels=texArrayDesc.MipLevels;

srvDesc.Texture2DArray.MostDetailedMip=0;

srvDesc.Texture2DArray.ArraySize=4;

srvDesc.Texture2DArray.FirstArraySlice=0;

srvDesc.ViewDimension=D3D11_SRV_DIMENSION_TEXTURE2DARRAY;

IFR(m_pDevice->CreateShaderResourceView(texArray,&srvDesc,&m_pTreeMapRV));

SAFE_RELEASE(texArray);

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

SAFE_RELEASE(srcTex[i]);

return hr;

}

 

  最后我把Vertex,Geometry,还有特别负责绘制的Pixel Shader的具体代码都贴出来。

VS_OUT VS(VS_IN vIn)

{

VS_OUT vOut;

vOut.posW=vIn.posW;

vOut.sizeW=vIn.sizeW;

return vOut;

}

 

[maxvertexcount(4)]

void GS(point VS_OUT gIn[1],uint primID:SV_PrimitiveID,inout TriangleStream<GS_OUT> triStream)

{

float halfWidth=0.5f*gIn[0].sizeW.x;

float halfHeight=0.5f*gIn[0].sizeW.y;

float4 v[4];

v[0]=float4(-halfWidth,-halfHeight,0.0f,1.0f);

v[1]=float4(halfWidth,-halfHeight,0.0f,1.0f);

v[2]=float4(-halfWidth,halfHeight,0.0f,1.0f);

v[3]=float4(halfWidth,halfHeight,0.0f,1.0f);

float2 texC[4];

texC[0]=float2(0.0f,1.0f);

texC[1]=float2(1.0f,1.0f);

texC[2]=float2(0.0f,0.0f);

texC[3]=float2(1.0f,0.0f);

float3 up=float3(0.0f,1.0f,0.0f);

float3 look=gEyePosW-gIn[0].posW;

look.y=0.0f;

look=normalize(look);

float3 right=cross(up,look);

matrix world;

world[0]=float4(right,0.0f);

world[1]=float4(up,0.0f);

world[2]=float4(look,0.0f);

world[3]=float4(gIn[0].posW,1.0f);

GS_OUT gOut;

//[unroll]

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

{

gOut.posW=mul(v[i],world);

gOut.posH=mul(v[i],world);

gOut.posH=mul(gOut.posH,View);

gOut.posH=mul(gOut.posH,Projection);

gOut.normalW=look;

gOut.texC=texC[i];

gOut.primID=primID;

triStream.Append(gOut);

}

}

 

float4 PS(GS_OUT pIn):SV_Target

{

float3 uvw=float3(pIn.texC,pIn.primID%4);

float4 diffuse=gDiffuseMap.Sample(gLinearSam,uvw);

clip(diffuse.a-0.5f);

return diffuse;

//return float4(1.0f,0.0f,0.0f,0.5f);

}

    最后实验截图,我们的树都是公告板生成的。

Directx11:基于GPU_GeometryShader的Billboard公告板绘制_第1张图片

 

 

你可能感兴趣的:(Directx11:基于GPU_GeometryShader的Billboard公告板绘制)