Pixel Shaders, Output Merger & Blending

Integer render targets need uint4/int4 outputs.

17.1 Outputs

Integer render targets need uint4/int4 outputs.

// Single render target
float4 PS(VSOut i) : SV_Target { return float4(1, 0, 0, 1); }

// Multiple render targets (G-buffer)
struct GBufferOut
{
    float4 albedo   : SV_Target0;   // RGBA8_UNORM_SRGB
    float4 normal   : SV_Target1;   // RGB10A2_UNORM
    float2 material : SV_Target2;   // RG8_UNORM: roughness, metallic
};

GBufferOut GBufferPS(VSOut i)
{
    GBufferOut o;
    o.albedo   = float4(0.8, 0.2, 0.2, 1);
    o.normal   = float4(normalize(i.normalW) * 0.5 + 0.5, 0);
    o.material = float2(0.4, 0.0);
    return o;
}

17.2 Discard, clip & alpha testing

Discarding disables some early-depth optimizations; prefer alpha-to-coverage with MSAA for foliage, or a depth prepass.

Texture2D gTex : register(t0);
SamplerState gSamp : register(s0);

float4 AlphaTestPS(VSOut i) : SV_Target
{
    float4 c = gTex.Sample(gSamp, i.uv);
    clip(c.a - 0.5);        // discard when alpha < 0.5
    return c;
}

17.3 Depth output & early-Z

Writing SV_Depth disables early depth testing. If you only push depth one way, use SV_DepthGreaterEqual/SV_DepthLessEqual. [earlydepthstencil] forces early-Z even when the PS writes UAVs:

RWStructuredBuffer<uint> gVisible : register(u1);

[earlydepthstencil]
float4 VisibilityPS(float4 pos : SV_Position, nointerpolation uint id : OBJECT_ID) : SV_Target
{
    gVisible[id] = 1;       // only executed for pixels passing the depth test
    return 0;
}

17.4 Blending (host state)

Blending is fixed-function, configured in D3D12_BLEND_DESC/D3D11_BLEND_DESC:

Dual-source (SV_Target0 + SV_Target1) Premultiplied alpha (return float4(color * alpha, alpha);) composes correctly with filtering and mipmaps.

17.5 Visual debugging with color

Output intermediate values to see them: return float4(N * 0.5 + 0.5, 1); (normals), return float4(i.uv, 0, 1); (UVs), return float4(frac(i.posW), 1); (world position), heatmaps for counts or costs.