HLSL изменяет глубину в пиксельном шейдере - PullRequest
0 голосов
/ 02 июня 2018

Мне нужно сделать изображение (с глубиной), которое я получаю извне.Я могу без проблем построить две текстуры и передать их в шейдер (могу проверить правильность значений, выбранных в пиксельном шейдере).

Вот как выглядит мой HLSL:

// image texture
Texture2D m_TextureColor : register(t0);

// depth texture with values [0..1]
Texture2D<float> m_TextureDepth : register(t1);

// sampler to forbid linear filtering since we're dealing with pixels
SamplerState m_TextureSampler { Filter = MIN_MAG_MIP_POINT; };

struct VS_IN
{
    float4 position : POSITION;
    float2 texcoord : TEXCOORD;
};

struct VS_OUT
{
    float4 position : SV_POSITION;
    float2 texcoord : TEXCOORD0;
};

struct PS_OUT
{
    float4 color : COLOR0;
    float depth : DEPTH0;
};

VS_OUT VS(VS_IN input)
{
    VS_OUT output = (VS_OUT)0;

    output.position = input.position;
    output.texcoord = input.texcoord;

    return output;
}

PS_OUT PS(VS_OUT input) : SV_Target
{
    PS_OUT output = (PS_OUT)0;

    output.color = m_TextureColor.SampleLevel(m_TextureSampler, input.texcoord, 0);

    // I want to modify depth of the pixel,
    // but it looks like it has no effect on depth no matter what I set here
    output.depth = m_TextureDepth.SampleLevel(m_TextureSampler, input.texcoord, 0);

    return output;
}

Я строю буфер вершин из тех (с PrimitiveTopology.TriangleStrip), где первый аргумент Vector4 является позицией, а второй аргумент Vector2 является координатой текстуры:

new[]
{
    new Vertex(new Vector4(-1, -1, 0.5f, 1), new Vector2(0, 1)),
    new Vertex(new Vector4(-1, 1, 0.5f, 1), new Vector2(0, 0)),
    new Vertex(new Vector4(1, -1, 0.5f, 1), new Vector2(1, 1)),
    new Vertex(new Vector4(1, 1, 0.5f, 1), new Vector2(1, 0)),
}

Все работает просто отлично: я вижу свое изображениеЯ могу сэмплировать глубину из текстуры глубины и создать из нее что-то визуальное (именно так я могу убедиться, что значения глубины, которые я выбираю, верны).Однако я не могу понять, как изменить глубину пикселя, чтобы он был съеден правильно, когда будет проходить тест глубины.Потому что на данный момент все зависит от того, какое значение z я задаю в качестве позиции вершины.

Вот как я настраиваю DirectX11 (я использую SharpDX и C#):

var swapChainDescription = new SwapChainDescription
{
    BufferCount = 1,
    ModeDescription = new ModeDescription(bufferSize.Width, bufferSize.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm),
    IsWindowed = true,
    OutputHandle = HostHandle,
    SampleDescription = new SampleDescription(1, 0),
    SwapEffect = SwapEffect.Discard,
    Usage = Usage.RenderTargetOutput,
};
var swapChainFlags = DeviceCreationFlags.None | DeviceCreationFlags.BgraSupport;
SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware, swapChainFlags, swapChainDescription, out var device, out var swapchain);

Настройка резервного буфера и буфера глубины / трафарета:

// color buffer
using (var textureColor = SwapChain.GetBackBuffer<Texture2D>(0))
{
    TextureColorResourceView = new RenderTargetView(Device, textureColor);
}

// depth buffer
using (var textureDepth = new Texture2D(Device, new Texture2DDescription
{
    Format = Format.D32_Float,
    ArraySize = 1,
    MipLevels = 1,
    Width = BufferSize.Width,
    Height = BufferSize.Height,
    SampleDescription = new SampleDescription(1, 0),
    Usage = ResourceUsage.Default,
    BindFlags = BindFlags.DepthStencil,
    CpuAccessFlags = CpuAccessFlags.None,
    OptionFlags = ResourceOptionFlags.None
}))
{
    TextureDepthResourceView = new DepthStencilView(Device, textureDepth);
}

DeviceContext.OutputMerger.SetTargets(TextureDepthResourceView, TextureColorResourceView);

Подготовка состояния трафарета глубины:

var description = DepthStencilStateDescription.Default();
description.DepthComparison = Comparison.LessEqual;
description.IsDepthEnabled = true;
description.DepthWriteMask = DepthWriteMask.All;
DepthState = new DepthStencilState(Device, description);

И использование его:

DeviceContext.OutputMerger.SetDepthStencilState(DepthState);

Вот как я создаю свои текстуры цвета / глубины, которые я отправляю в шейдер:

public static (ShaderResourceView resource, Texture2D texture) CreateTextureDynamic(this Device device, System.Drawing.Size size, Format format)
{
    var textureDesc = new Texture2DDescription
    {
        MipLevels = 1,
        Format = format,
        Width = size.Width,
        Height = size.Height,
        ArraySize = 1,
        BindFlags = BindFlags.ShaderResource,
        Usage = ResourceUsage.Dynamic,
        SampleDescription = new SampleDescription(1, 0),
        CpuAccessFlags = CpuAccessFlags.Write,
    };

    var texture = new Texture2D(device, textureDesc);
    return (new ShaderResourceView(device, texture), texture);
}

Также, поскольку мне нужно часто обновлять их:

public static void UpdateResource(this Texture2D texture, int[] buffer, System.Drawing.Size size)
{
    var dataBox = texture.Device.ImmediateContext.MapSubresource(texture, 0, MapMode.WriteDiscard, MapFlags.None, out var dataStream);
    Parallel.For(0, size.Height, rowIndex => Marshal.Copy(buffer, size.Width * rowIndex, dataBox.DataPointer + dataBox.RowPitch * rowIndex, size.Width));
    dataStream.Dispose();
    texture.Device.ImmediateContext.UnmapSubresource(texture, 0);
}

public static void UpdateResource(this Texture2D texture, float[] buffer, System.Drawing.Size size)
{
    var dataBox = texture.Device.ImmediateContext.MapSubresource(texture, 0, MapMode.WriteDiscard, MapFlags.None, out var dataStream);
    Parallel.For(0, size.Height, rowIndex => Marshal.Copy(buffer, size.Width * rowIndex, dataBox.DataPointer + dataBox.RowPitch * rowIndex, size.Width));
    dataStream.Dispose();
    texture.Device.ImmediateContext.UnmapSubresource(texture, 0);
}

Iтакже много гуглил по этому поводу, нашел похожие посты вот так: https://www.gamedev.net/forums/topic/573961-how-to-set-depth-value-in-pixel-shader/ однако не смог решить это на моей стороне.

Заранее спасибо!

1 Ответ

0 голосов
/ 05 июня 2018

Чтобы записать в буфер глубины, вам нужно настроить семантическое значение системного значения SV_Depth.Таким образом, ваша структура вывода пиксельных шейдеров будет выглядеть примерно так:

struct PS_OUT
{
    float4 color : SV_Target;
    float depth : SV_Depth;
};

И шейдер не будет указывать SV_Target, как в вашем примере (выходные данные SV_ определены в структуре).Таким образом, это выглядело бы следующим образом:

PS_OUT PS(VS_OUT input)
{
    PS_OUT output = (PS_OUT)0;

    output.color = m_TextureColor.SampleLevel(m_TextureSampler, input.texcoord, 0);

    // Now that output.depth is defined with SV_Depth, and you have depth-write enabled,
    // this should write to the depth buffer.
    output.depth = m_TextureDepth.SampleLevel(m_TextureSampler, input.texcoord, 0);

    return output;
}

Обратите внимание, что вы можете понести некоторые потери производительности при явной глубокой записи (особенно на аппаратных средствах AMD), поскольку это вызывает обход их ранней аппаратной оптимизации.Во всех будущих вызовах отрисовки с использованием этого буфера глубины будет отключена оптимизация раннего Z, поэтому, как правило, рекомендуется выполнять операцию глубинной записи как можно позже.

...