• 热门专题

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

作者:qiul12345  发布日期:2011-11-22 16:19:05
Tag标签:GeometryShader  Directx11  
  •     公告板是游戏里一很常见的技术,用来绘制树木,草地,爆炸效果等。说白了就求一个矩形框,然后贴一张照片上去。(Ps下由于个人博客所以讲的比较随便,有时候讲billboard,有时候公告板,有时候一个矩形框,都指差不多一个东西)

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

        这里用个Demo阐述下如何用GeomtryShader在GPU中绘制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里。Directx11用ID3D11Texture2D 表示这个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);
    
    }
    
    

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

     

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规