-
公告闆是遊戲裡一很常見的技術,用來繪制樹木,草地,爆炸效果等。說白了就求一個矩形框,然後貼一張照片上去。(Ps下由于個人部落格是以講的比較随便,有時候講billboard,有時候公告闆,有時候一個矩形框,都指差不多一個東西)
而Geometry Shader則是Directx10裡面推出的一個新的Shader能夠處理完整的幾何模型。想典型VertexShader隻知道并處理頂點,而Geometry Shader知道頂點,直線和三角形并處理它們。
這裡用個Demo闡述下如何用GeomtryShader在GPU中繪制Billboard公告闆。
1先講下求公告闆的算法。說白了就是求它的世界坐标系。
假設這個公告闆上方的向量是v,面向我們眼睛向量w,公告闆右手方向u。公告闆中心位置為C,我們眼睛位置為E,則有:
用圖表達下場景:Directx11:基于GeometryShader的Billboard公告闆繪制 則該公告闆的世界坐标系為Directx11:基于GeometryShader的Billboard公告闆繪制 Directx11:基于GeometryShader的Billboard公告闆繪制 知道了其算法後,在GeomtryShader中我們主要根據傳進來的公告闆的中心點位置,求它的世界矩坐标系轉換陣。
view source print ?
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.
具體函數如下:
view source print ?
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的具體代碼都貼出來。
view source print ?
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.
最後實驗截圖,我們的樹都是公告闆生成的。}
Directx11:基于GeometryShader的Billboard公告闆繪制
延伸閱讀:
- 1、Directx11基于GeometryShader的粒子系統
- 2、Directx11與PhysX的結合
- 3、Directx11_使用Effect架構包裝ComputeShader
- 4、Directx11_使用Effect架構包裝ComputeShader