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

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

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

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

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

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

     \

        用图表达下场景:

     \

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

     \

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

     

    01. [maxvertexcount(4)]
    02.  
    03. void GS(point VS_OUT gIn[1],uint primID:SV_PrimitiveID,inout TriangleStream<GS_OUT> triStream)
    04.  
    05. {
    06.  
    07. float halfWidth=0.5f*gIn[0].sizeW.x;
    08.  
    09. float halfHeight=0.5f*gIn[0].sizeW.y;
    10.  
    11. float4 v[4];
    12.  
    13. v[0]=float4(-halfWidth,-halfHeight,0.0f,1.0f);
    14.  
    15. v[1]=float4(halfWidth,-halfHeight,0.0f,1.0f);
    16.  
    17. v[2]=float4(-halfWidth,halfHeight,0.0f,1.0f);
    18.  
    19. v[3]=float4(halfWidth,halfHeight,0.0f,1.0f);
    20.  
    21. float2 texC[4];
    22.  
    23. texC[0]=float2(0.0f,1.0f);
    24.  
    25. texC[1]=float2(1.0f,1.0f);
    26.  
    27. texC[2]=float2(0.0f,0.0f);
    28.  
    29. texC[3]=float2(1.0f,0.0f);
    30.  
    31. float3 up=float3(0.0f,1.0f,0.0f);
    32.  
    33. float3 look=gEyePosW-gIn[0].posW;
    34.  
    35. look.y=0.0f;
    36.  
    37. look=normalize(look);
    38.  
    39. float3 right=cross(up,look);
    40.  
    41. matrix world;
    42.  
    43. world[0]=float4(right,0.0f);
    44.  
    45. world[1]=float4(up,0.0f);
    46.  
    47. world[2]=float4(look,0.0f);
    48.  
    49. world[3]=float4(gIn[0].posW,1.0f);
    50.  
    51. GS_OUT gOut;
    52.  
    53. //[unroll]
    54.  
    55. for(int i=0;i<4;i++)
    56.  
    57. {
    58.  
    59. gOut.posW=mul(v[i],world);
    60.  
    61. gOut.posH=mul(v[i],world);
    62.  
    63. gOut.posH=mul(gOut.posH,View);
    64.  
    65. gOut.posH=mul(gOut.posH,Projection);
    66.  
    67. gOut.normalW=look;
    68.  
    69. gOut.texC=texC[i];
    70.  
    71. gOut.primID=primID;
    72.  
    73. triStream.Append(gOut);
    74.  
    75. }
    76.  
    77. }

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

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

        创建步骤:

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

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

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

        4为texArray创建一个shader resource view.

        具体函数如下:

     

    001. HRESULT Tree::BuildShaderResourceView()
    002.  
    003. {
    004.  
    005. HRESULT hr=S_OK;
    006.  
    007. std::wstring filenames[4]={ L"tree0.dds",L"tree1.dds",L"tree2.dds",L"tree3.dds" };
    008.  
    009. // 1读入图片,为每一张图片创建一个ID3D11Texture2D srcTex[i]。
    010.  
    011. ID3D11Texture2D* srcTex[4];
    012.  
    013. for(UINT i=0;i<4;i++)
    014.  
    015. {
    016.  
    017. D3DX11_IMAGE_LOAD_INFO loadInfo;
    018.  
    019. loadInfo.Width=D3DX11_DEFAULT;
    020.  
    021. loadInfo.Height=D3DX11_DEFAULT;
    022.  
    023. loadInfo.Depth=D3DX11_DEFAULT;
    024.  
    025. loadInfo.CpuAccessFlags=D3D11_CPU_ACCESS_READ;
    026.  
    027. loadInfo.BindFlags=0;
    028.  
    029. loadInfo.Filter=D3DX11_FILTER_NONE;
    030.  
    031. loadInfo.MipFilter=D3DX11_FILTER_NONE;
    032.  
    033. loadInfo.FirstMipLevel=0;
    034.  
    035. loadInfo.Format=DXGI_FORMAT_R8G8B8A8_UNORM;
    036.  
    037. loadInfo.MipLevels=D3DX11_DEFAULT;
    038.  
    039. loadInfo.MiscFlags=0;
    040.  
    041. loadInfo.Usage=D3D11_USAGE_STAGING;
    042.  
    043. loadInfo.pSrcInfo=0;
    044.  
    045. IFR(D3DX11CreateTextureFromFile(m_pDevice,filenames[i].c_str(),&loadInfo,NULL,(ID3D11Resource**)&srcTex[i],NULL));
    046.  
    047. }
    048.  
    049. //2创建用来存储上面所有纹理的纹理数组ID3D11Texture2D texArray;
    050.  
    051. D3D11_TEXTURE2D_DESC texDesc;
    052.  
    053. srcTex[0]->GetDesc(&texDesc);
    054.  
    055. D3D11_TEXTURE2D_DESC texArrayDesc;
    056.  
    057. texArrayDesc.Width=texDesc.Width;
    058.  
    059. texArrayDesc.Height=texDesc.Height;
    060.  
    061. texArrayDesc.MipLevels=texDesc.MipLevels;
    062.  
    063. texArrayDesc.ArraySize=4;
    064.  
    065. texArrayDesc.BindFlags=D3D11_BIND_SHADER_RESOURCE;
    066.  
    067. texArrayDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;
    068.  
    069. texArrayDesc.CPUAccessFlags=0;
    070.  
    071. texArrayDesc.SampleDesc.Count=1;
    072.  
    073. texArrayDesc.SampleDesc.Quality=0;
    074.  
    075. texArrayDesc.MiscFlags=0;
    076.  
    077. texArrayDesc.Usage=D3D11_USAGE_DEFAULT;
    078.  
    079. ID3D11Texture2D *texArray=NULL;
    080.  
    081. IFR(m_pDevice->CreateTexture2D(&texArrayDesc,NULL,&texArray));
    082.  
    083. //3将每个纹理srcTex[i]拷贝到texArray相应的位置。
    084.  
    085. for(int i=0;i<4;i++)
    086.  
    087. {
    088.  
    089. for(int j=0;j<texDesc.MipLevels;j++)
    090.  
    091. {
    092.  
    093. D3D11_MAPPED_SUBRESOURCE subTex;
    094.  
    095. m_pContext->Map(srcTex[i],j,D3D11_MAP_READ,0,&subTex);
    096.  
    097. m_pContext->UpdateSubresource(texArray,D3D11CalcSubresource(j,i,texDesc.MipLevels),NULL,subTex.pData,subTex.RowPitch,0);
    098.  
    099. m_pContext->Unmap(srcTex[i],j);
    100.  
    101. }
    102.  
    103. }
    104.  
    105. //4为texArray创建一个shader resource view.
    106.  
    107. D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
    108.  
    109. ZeroMemory(&srvDesc,sizeof(srvDesc));
    110.  
    111. srvDesc.Format=texArrayDesc.Format;
    112.  
    113. srvDesc.Texture2DArray.MipLevels=texArrayDesc.MipLevels;
    114.  
    115. srvDesc.Texture2DArray.MostDetailedMip=0;
    116.  
    117. srvDesc.Texture2DArray.ArraySize=4;
    118.  
    119. srvDesc.Texture2DArray.FirstArraySlice=0;
    120.  
    121. srvDesc.ViewDimension=D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
    122.  
    123. IFR(m_pDevice->CreateShaderResourceView(texArray,&srvDesc,&m_pTreeMapRV));
    124.  
    125. SAFE_RELEASE(texArray);
    126.  
    127. for(int i=0;i<4;i++)
    128.  
    129. SAFE_RELEASE(srcTex[i]);
    130.  
    131. return hr;
    132.  
    133. }

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

     

    001. VS_OUT VS(VS_IN vIn)
    002.  
    003. {
    004.  
    005. VS_OUT vOut;
    006.  
    007. vOut.posW=vIn.posW;
    008.  
    009. vOut.sizeW=vIn.sizeW;
    010.  
    011. return vOut;
    012.  
    013. }
    014.  
    015.  
    016.  
    017. [maxvertexcount(4)]
    018.  
    019. void GS(point VS_OUT gIn[1],uint primID:SV_PrimitiveID,inout TriangleStream<GS_OUT> triStream)
    020.  
    021. {
    022.  
    023. float halfWidth=0.5f*gIn[0].sizeW.x;
    024.  
    025. float halfHeight=0.5f*gIn[0].sizeW.y;
    026.  
    027. float4 v[4];
    028.  
    029. v[0]=float4(-halfWidth,-halfHeight,0.0f,1.0f);
    030.  
    031. v[1]=float4(halfWidth,-halfHeight,0.0f,1.0f);
    032.  
    033. v[2]=float4(-halfWidth,halfHeight,0.0f,1.0f);
    034.  
    035. v[3]=float4(halfWidth,halfHeight,0.0f,1.0f);
    036.  
    037. float2 texC[4];
    038.  
    039. texC[0]=float2(0.0f,1.0f);
    040.  
    041. texC[1]=float2(1.0f,1.0f);
    042.  
    043. texC[2]=float2(0.0f,0.0f);
    044.  
    045. texC[3]=float2(1.0f,0.0f);
    046.  
    047. float3 up=float3(0.0f,1.0f,0.0f);
    048.  
    049. float3 look=gEyePosW-gIn[0].posW;
    050.  
    051. look.y=0.0f;
    052.  
    053. look=normalize(look);
    054.  
    055. float3 right=cross(up,look);
    056.  
    057. matrix world;
    058.  
    059. world[0]=float4(right,0.0f);
    060.  
    061. world[1]=float4(up,0.0f);
    062.  
    063. world[2]=float4(look,0.0f);
    064.  
    065. world[3]=float4(gIn[0].posW,1.0f);
    066.  
    067. GS_OUT gOut;
    068.  
    069. //[unroll]
    070.  
    071. for(int i=0;i<4;i++)
    072.  
    073. {
    074.  
    075. gOut.posW=mul(v[i],world);
    076.  
    077. gOut.posH=mul(v[i],world);
    078.  
    079. gOut.posH=mul(gOut.posH,View);
    080.  
    081. gOut.posH=mul(gOut.posH,Projection);
    082.  
    083. gOut.normalW=look;
    084.  
    085. gOut.texC=texC[i];
    086.  
    087. gOut.primID=primID;
    088.  
    089. triStream.Append(gOut);
    090.  
    091. }
    092.  
    093. }
    094.  
    095.  
    096.  
    097. float4 PS(GS_OUT pIn):SV_Target
    098.  
    099. {
    100.  
    101. float3 uvw=float3(pIn.texC,pIn.primID%4);
    102.  
    103. float4 diffuse=gDiffuseMap.Sample(gLinearSam,uvw);
    104.  
    105. clip(diffuse.a-0.5f);
    106.  
    107. return diffuse;
    108.  
    109. //return float4(1.0f,0.0f,0.0f,0.5f);
    110.  
    111. }

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

     \

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值