• 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);

    }

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

  • 相关阅读:
    Linux目录结构
    Linux简介
    队列、生产者消费者模型
    Process的几个用法和守护进程
    并发编程(初学)
    网络编程知识点小结
    用socketserver模块实现并发
    粘包问题、解决粘包问题和struct模块
    模拟ssh功能和subprocess模块
    socket 套接字编程
  • 原文地址:https://www.cnblogs.com/bester/p/3255789.html
Copyright © 2020-2023  润新知