|
This will be my first blog post, I'm really new at this so I'm sorry if this sucks!.
Dynamic Cubemaps with SharpDX using the Geometry shader and instancing:
First we need to setup a few things up:
Cubemap texture.
Cubemap renderarget.
Cubemap shaderresourceview.
Cubemap DepthStencil.
Dim dstex As New Texture2DDescription
dstex.Width = 512
dstex.Height = 512
dstex.MipLevels = 1
dstex.ArraySize = 6
dstex.SampleDescription = New SampleDescription(1, 0)
dstex.Format = Format.R32_Typeless
dstex.Usage = ResourceUsage.Default
dstex.BindFlags = BindFlags.ShaderResource Or BindFlags.DepthStencil
dstex.CPUAccessFlags = 0
dstex.OptionFlags = ResourceOptionFlags.TextureCube
g_pEnvMapDepth = CreateTexture("CubeDepth)", dstex)
Dim DescDS As New DepthStencilViewDescription
DescDS.Format = Format.D32_Float
DescDS.Dimension = DepthStencilViewDimension.Texture2DArray
DescDS.Texture2DArray.FirstArraySlice = 0
DescDS.Texture2DArray.ArraySize = 6
DescDS.Texture2DArray.MipSlice = 0
g_pEnvMapDSV = New DepthStencilView(Engine.Device, g_pEnvMapDepth, DescDS)
DescDS.Texture2DArray.ArraySize = 1
g_pEnvMapOneDSV = New DepthStencilView(Engine.Device, g_pEnvMapDepth, DescDS)
dstex.Format = Format.R8G8B8A8_SNorm
dstex.BindFlags = BindFlags.RenderTarget Or BindFlags.ShaderResource Or BindFlags.UnorderedAccess
dstex.OptionFlags = ResourceOptionFlags.TextureCube
dstex.MipLevels = 1
g_pEnvMap = CreateTexture("g_pEnvMap)", dstex)
Dim DescRT As New RenderTargetViewDescription
DescRT.Format = dstex.Format
DescRT.Dimension = RenderTargetViewDimension.Texture2DArray
DescRT.Texture2DArray.FirstArraySlice = 0
DescRT.Texture2DArray.ArraySize = 6
DescRT.Texture2DArray.MipSlice = 0
g_pEnvMapRTV = New RenderTargetView(Engine.Device, g_pEnvMap, DescRT)
Dim SRVDesc As New ShaderResourceViewDescription
SRVDesc.Format = dstex.Format
SRVDesc.Dimension = ShaderResourceViewDimension.TextureCube
SRVDesc.TextureCube.MipLevels = 1
SRVDesc.TextureCube.MostDetailedMip = 0
g_pEnvMapSRV = CreateShaderResourceView("FinishedCubeMap" & i, g_pEnvMap, SRVDesc)
Dim SRVDesc2 As New ShaderResourceViewDescription
SRVDesc2.Format = dstex.Format
SRVDesc2.Dimension = ShaderResourceViewDimension.Texture2DArray
SRVDesc2.Texture2DArray.MipLevels = 1
SRVDesc2.Texture2DArray.MostDetailedMip = 0
SRVDesc2.Texture2DArray.ArraySize = 1
Dim i As Integer = 0
While i < 6
DescRT.Texture2DArray.FirstArraySlice = i
SRVDesc2.Texture2DArray.FirstArraySlice = i
g_apEnvMapOneSRV(i) = CreateShaderResourceView("g_apEnvMapOneSRV_" & i, g_pEnvMap, SRVDesc2)
System.Threading.Interlocked.Increment(i)
End While
We now have a Cubemap that we can draw into.
We need to set up the view matrix for each face of the cubemap, this will be done each frame or every 2nd frame.
Dim temp As Matrix
temp = Matrix.RotationX(MathHelper.ToRadians(90)) * Matrix.RotationY(MathHelper.ToRadians(0)) * Matrix.Translation(_Pos)
CubeUpView = Matrix.Invert(temp)
temp = Matrix.RotationX(MathHelper.ToRadians(-90)) * Matrix.RotationY(MathHelper.ToRadians(0)) * Matrix.Translation(_Pos)
CubeDownView = Matrix.Invert(temp)
temp = Matrix.RotationX(MathHelper.ToRadians(0)) * Matrix.RotationY(MathHelper.ToRadians(90)) * Matrix.Translation(_Pos)
CubeLeftView = Matrix.Invert(temp)
temp = Matrix.RotationX(MathHelper.ToRadians(0)) * Matrix.RotationY(MathHelper.ToRadians(-90)) * Matrix.Translation(_Pos)
CubeRightView = Matrix.Invert(temp)
temp = Matrix.RotationX(MathHelper.ToRadians(0)) * Matrix.RotationY(MathHelper.ToRadians(0)) * Matrix.Translation(_Pos)
CubeForwardView = Matrix.Invert(temp)
temp = Matrix.RotationX(MathHelper.ToRadians(0)) * Matrix.RotationY(MathHelper.ToRadians(180)) * Matrix.Translation(_Pos)
CubeBackView = Matrix.Invert(temp)
When its time to draw into the cubemap I store each of the cube view matrix in an array and set it as a Cbuffer in the shader I'm drawing with.
_context.OutputMerger.SetTargets(g_pEnvMapDSV, g_pEnvMapRTV)
_context.ClearDepthStencilView(g_pEnvMapDSV, DepthStencilClearFlags.Depth, 1, 0)
_context.ClearRenderTargetView(g_pEnvMapRTV, New Color4(0, 0, 0, 0))
_context.OutputMerger.DepthStencilState = Default
Dim old As ViewportF = _context.Rasterizer.GetViewports(0)
CubeViews(0) = CubeRightView
CubeViews(1) = CubeLeftView
CubeViews(2) = CubeUpView
CubeViews(3) = CubeDownView
CubeViews(4) = CubeForwardView
CubeViews(5) = CubeBackView
_context.Rasterizer.SetViewport(ReflectionViewport)
_effect.GetVariableByName("CubeViews").AsMatrix().SetMatrix(CubeViews)
DrawIndexedInstanced(6)
That is about all that is needed on the CPU side of things.
The hard part for me was getting the shaders set up, the basic idea is to draw with instancing and use a pass through geometry shader with SV_RenderTargetArrayIndex to select the correct render target based on the runtime generated SV_InstanceID, the Instance ID is also used to select the View matrix.
struct VertexShaderInput
{
float3 Position : POSITION0;
row_major float4x4 World : WORLD;
uint CubeIndex : SV_InstanceID;
};
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
uint RTIndex : RTARRAYINDEX;
};
struct VertexShaderOutput2
{
float4 Position : SV_POSITION;
uint RTIndex : SV_RenderTargetArrayIndex;
};
matrix CubeViews[6];
VertexShaderOutput VShader(VertexShaderInput input)
{
VertexShaderOutput output=(VertexShaderOutput)0;
float4x4 view = CubeViews[input.CubeIndex];
float4x4 VP = mul(input.World, view);
float4x4 WVP = mul(VP, Projection);
output.Position = mul(float4(input.Position,1), WVP);
output.RTIndex = input.CubeIndex;
return output;
}
[maxvertexcount(3)]
void GS_CubeMap_Inst(triangle VertexShaderOutput input[3], inout TriangleStream<VertexShaderOutput2> CubeMapStream)
{
VertexShaderOutput2 output;
for (int v = 0; v < 3; v++)
{
output.Position = input[v].Position;
output.RTIndex = input[v].RTIndex;
CubeMapStream.Append(output);
}
}
This is not a full code listing, I'm just going over the parts that I found in the DirectX SDK and converted to SharpDX.
This method for drawing a dynamic cube map is fast but there is still a noticeable drop in frames when doing this, another way would be do drop the instancing and the geometry shader then just spread the face drawing over 6 frames but then it wouldn't be fully dynamic.
The small size of the cube map also sees a very big drop in image quality but as I'm using it for the ambient term in my lighting it looks very nice.
CodeProject
modified 21-Oct-15 7:58am.
|
|
|
|