Shader "Builtin Object Id Outline" { HLSLINCLUDE // XC_BUILTIN_OBJECT_ID_OUTLINE_D3D12_SHARED cbuffer OutlineConstants { float4 gViewportSizeAndTexelSize; float4 gOutlineColor; float4 gSelectedInfo; float4 gSelectedObjectColors[256]; }; Texture2D gObjectIdTexture; struct VSOutput { float4 position : SV_POSITION; }; VSOutput MainVS(uint vertexId : SV_VertexID) { // XC_BUILTIN_OBJECT_ID_OUTLINE_D3D12_VS static const float2 positions[3] = { float2(-1.0, -1.0), float2(-1.0, 3.0), float2( 3.0, -1.0) }; VSOutput output; output.position = float4(positions[vertexId], 0.0, 1.0); return output; } int2 ClampPixelCoord(int2 pixelCoord) { const int2 maxCoord = int2( max((int)gViewportSizeAndTexelSize.x - 1, 0), max((int)gViewportSizeAndTexelSize.y - 1, 0)); return clamp(pixelCoord, int2(0, 0), maxCoord); } float4 LoadObjectId(int2 pixelCoord) { return gObjectIdTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)); } bool IsSelectedObject(float4 objectIdColor) { // Object-id surfaces encode the low 32 bits across RGBA, so low-valued // runtime ids legitimately have zero alpha. Only the all-zero clear // color should be treated as "no object". if (all(abs(objectIdColor) <= float4( 0.0025, 0.0025, 0.0025, 0.0025))) { return false; } const int selectedCount = min((int)gSelectedInfo.x, 256); [loop] for (int i = 0; i < selectedCount; ++i) { const float4 selectedColor = gSelectedObjectColors[i]; if (all(abs(objectIdColor - selectedColor) <= float4( 0.0025, 0.0025, 0.0025, 0.0025))) { return true; } } return false; } float4 MainPS(VSOutput input) : SV_TARGET { // XC_BUILTIN_OBJECT_ID_OUTLINE_D3D12_PS const int2 pixelCoord = int2(input.position.xy); const bool debugSelectionMask = gSelectedInfo.y > 0.5; const bool centerSelected = IsSelectedObject(LoadObjectId(pixelCoord)); if (debugSelectionMask) { return centerSelected ? float4(1.0, 1.0, 1.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); } if (centerSelected) { discard; } const int outlineWidth = max((int)gSelectedInfo.z, 1); float outline = 0.0; [loop] for (int y = -2; y <= 2; ++y) { [loop] for (int x = -2; x <= 2; ++x) { if (x == 0 && y == 0) { continue; } const float distancePixels = length(float2((float)x, (float)y)); if (distancePixels > outlineWidth) { continue; } if (!IsSelectedObject(LoadObjectId(pixelCoord + int2(x, y)))) { continue; } const float weight = saturate( 1.0 - ((distancePixels - 1.0) / max((float)outlineWidth, 1.0))); outline = max(outline, weight); } } if (outline <= 0.001) { discard; } return float4(gOutlineColor.rgb, gOutlineColor.a * outline); } ENDHLSL SubShader { Pass { Name "ObjectIdOutline" Tags { "LightMode" = "ObjectIdOutline" } Cull Off ZWrite Off ZTest Always Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha HLSLPROGRAM #pragma target 4.5 #pragma vertex MainVS #pragma fragment MainPS ENDHLSL } } }