I'm beginning with DirectX development and I'm quite confused by the documentation about how to do the following:
I have 1 image (Texture2D), I'd like to apply 2 independent HLSL, one after the other, and render it.
For instance, one shader makes the texture semi-transparent, the other turns it to black and white. Also, I'm computing everything off-screen, so I don't have a SwapChain.
So far the output texture I get has the 1st shader applied but not the second. If I switch the order where the shaders are applied, then I'm seeing the 1st shader applied (in the new order) but not the second. In other words, both shaders work separately but not together. I'm using SharpDX and C#.
Here is what I made
Setup
- Create D3D11 device.
- Set Blend state
BlendStateDescription blendStateDescription = new BlendStateDescription
{
AlphaToCoverageEnable = false,
IndependentBlendEnable = false
};
blendStateDescription.RenderTarget[0].IsBlendEnabled = true;
blendStateDescription.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;
blendStateDescription.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;
blendStateDescription.RenderTarget[0].SourceAlphaBlend = BlendOption.One;
blendStateDescription.RenderTarget[0].DestinationAlphaBlend = BlendOption.One;
blendStateDescription.RenderTarget[0].BlendOperation = BlendOperation.Add;
blendStateDescription.RenderTarget[0].AlphaBlendOperation = BlendOperation.Add;
blendStateDescription.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;
device.ImmediateContext.OutputMerger.SetBlendState(new BlendState(device, blendStateDescription));
- Set Depth stencil state
var depthStencilState = new DepthStencilState(device, DepthStencilStateDescription.Default());
device.ImmediateContext.OutputMerger.SetDepthStencilState(depthStencilState);
- Create a RenderTargetView and a texture
- Create DepthStencilView and a texture
- Set the RenderTargetView and DepthStencilView are targets of the output merger.
Run
- Load an image from the hard drive to a Texture2D
- Create a ShaderResourceView from this texture.
- For each effects I want to apply:
- Load the shader from the bytecode into an Effect.
- Create a vertice, then create a buffer from it.
- Set this buffer as a vertex buffer in the Input Assembler:
deviceContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(myBuffer, Marshal.SizeOf(typeof(VertexPositionTexture)), 0));
- Set the input layout.
- Get a
technique
from the shader and for each pass, IeffectPass.Apply
then do adeviceContext.Draw
.
deviceContext.Flush();
- Save the texture associated to the RenderTargetView as a PNG.
Question
Is my approach correct or am I doing something wrong in that method?
I wonder if the issue could be in the blend state or depth stencil. Let me know if you'd like me to share more code to see my implementation details.
Thank you in advance for the help. :)
[UPDATE 3/8/21]
Here is a simplified version of my code for insights:
var device = new Device(
DriverType.Hardware,
DeviceCreationFlags.BgraSupport | DeviceCreationFlags.VideoSupport,
FeatureLevel.Level_11_0);
BlendStateDescription blendStateDescription = new BlendStateDescription
{
AlphaToCoverageEnable = false,
};
blendStateDescription.RenderTarget[0].IsBlendEnabled = true;
blendStateDescription.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;
blendStateDescription.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;
blendStateDescription.RenderTarget[0].BlendOperation = BlendOperation.Add;
blendStateDescription.RenderTarget[0].SourceAlphaBlend = BlendOption.Zero;
blendStateDescription.RenderTarget[0].DestinationAlphaBlend = BlendOption.Zero;
blendStateDescription.RenderTarget[0].AlphaBlendOperation = BlendOperation.Add;
blendStateDescription.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;
device.ImmediateContext.OutputMerger.SetBlendState(new BlendState(device, blendStateDescription));
DepthStencilStateDescription description = DepthStencilStateDescription.Default();
description.DepthComparison = Comparison.LessEqual;
description.IsDepthEnabled = true;
device.ImmediateContext.OutputMerger.SetDepthStencilState(new DepthStencilState(device, description));
// [...]
Texture2DDescription depthDesc = new Texture2DDescription()
{
Width = _outputTextureSize.Width,
Height = _outputTextureSize.Height,
Format = Format.D24_UNorm_S8_UInt,
OptionFlags = ResourceOptionFlags.None,
ArraySize = 1,
MipLevels = 1,
SampleDescription =
{
Count = 1,
Quality = 0
},
BindFlags = BindFlags.DepthStencil,
CpuAccessFlags = CpuAccessFlags.None,
Usage = ResourceUsage.Default
};
DepthStencilViewDescription depthViewDesc = new DepthStencilViewDescription()
{
Dimension = DepthStencilViewDimension.Texture2D,
Format = Format.D24_UNorm_S8_UInt,
};
_depthStencilTexture = new Texture2D(device, depthDesc);
_depthStencilView = new DepthStencilView(device, _depthStencilTexture, depthViewDesc);
device.ImmediateContext.OutputMerger.SetDepthStencilState(
new DepthStencilState(device,
DepthStencilStateDescription.Default()));
// RENDERING
context.ClearRenderTargetView(_renderTargetView, Color.CornflowerBlue);
context.ClearDepthStencilView(_depthStencilView, DepthStencilClearFlags.Depth, 1f, 0);
using ShaderResourceView shaderResourceView = new ShaderResourceView(device, inputTexture2D)
foreach (var effect in effectList)
{
using ShaderBytecode shaderByteCode = ShaderBytecode.FromFile(effect.FilePath);
var shaderEffect = new Effect(device, shaderByteCode.Data);
using var verticesRectangle = new DataStream(
Marshal.SizeOf(typeof(VertexPositionTexture)) * 4,
true,
true);
// verticesRectangle.Write ...
verticesRectangle.Position = 0;
var buffer
= new Buffer(
device,
verticesRectangle,
new BufferDescription
{
BindFlags = BindFlags.VertexBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
SizeInBytes = Marshal.SizeOf(typeof(VertexPositionTexture)) * 4,
Usage = ResourceUsage.Default,
StructureByteStride = 0
});
device.ImmediateContext.InputAssembler.InputLayout
= new InputLayout(
device,
shaderEffect.GetTechniqueByIndex(0).GetPassByIndex(0).Description.Signature,
elements: VertexPositionTexture.InputElements);
device.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
device.ImmediateContext.InputAssembler.SetVertexBuffers(
0,
new VertexBufferBinding(
buffer,
Marshal.SizeOf(typeof(VertexPositionTexture)),
0));
for (var i = 0; i < shaderEffect.GetTechniqueByIndex(0).Description.PassCount; ++i)
{
using EffectPass effectPass = shaderEffect.GetTechniqueByIndex(0).GetPassByIndex(i);
if (effectPass.IsValid)
{
effectPass.Apply(deviceContext);
}
device.ImmediateContext.Draw(4, 0);
}
device.ImmediateContext.InputAssembler.InputLayout.Dispose();
device.ImmediateContext.InputAssembler.InputLayout = null;
}
device.ImmediateContext.Flush();
My shader effects:
///
/// Shader effect that changes the opacity of a texture.
///
Texture2D Texture : register(t0);
SamplerState TextureSampler : register(s0);
float TargetOpacity;
struct ShaderParameters
{
float4 Position : SV_Position; // vertex position input
float2 TextureUV : TEXCOORD0; // first texture coordinate input
};
// Vertex Shader
ShaderParameters VS(ShaderParameters input)
{
ShaderParameters output = (ShaderParameters) 0;
output.Position = input.Position;
output.TextureUV = input.TextureUV;
return output; // Returns the position without any change to it.
}
// Pixel Shader
float4 PS(ShaderParameters input) : SV_Target
{
float4 rgbaColor = Texture.Sample(TextureSampler, input.TextureUV);
return float4(rgbaColor.rgb, rgbaColor.a * TargetOpacity);
}
technique10 OpacityTechnique
{
pass P0
{
SetGeometryShader(0);
SetVertexShader(CompileShader(vs_5_0, VS()));
SetPixelShader(CompileShader(ps_5_0, PS()));
}
}
And
///
/// Shader effect that converts a colorful texture to monochrome (gray).
///
Texture2D Texture : register(t0);
SamplerState TextureSampler : register(s0);
struct ShaderParameters
{
float4 Position : SV_Position; // vertex position input
float2 TextureUV : TEXCOORD0; // first texture coordinate input
};
// Vertex Shader
ShaderParameters VS(ShaderParameters input)
{
ShaderParameters output = (ShaderParameters) 0;
output.Position = input.Position;
output.TextureUV = input.TextureUV;
return output; // Returns the position without any change to it.
}
// Pixel Shader
float4 PS(ShaderParameters input) : SV_Target
{
float4 rgbaColor = Texture.Sample(TextureSampler, input.TextureUV);
float gray = (rgbaColor.r + rgbaColor.g + rgbaColor.b) / 3;
return float4(gray, gray, gray, rgbaColor.a);
}
technique10 GrayscaleTechnique
{
pass P0
{
SetGeometryShader(0);
SetVertexShader(CompileShader(vs_5_0, VS()));
SetPixelShader(CompileShader(ps_5_0, PS()));
}
}
Thanks :)
deviceContext.ClearDepthStencilView(_depthStencilView, DepthStencilClearFlags.Depth, 1f, 0);
. I placed this list thedeviceContext.Draw(verticeCount, 0)
. Now I'm seeing the opposite issue. The second effect is applied to the output texture but not the first one. I'd assume the first one got overwrote by the second one. I'm not sure to understand why. \$\endgroup\$