Files
XCEngine/engine/assets/builtin/shaders/object-id-outline.shader

141 lines
3.9 KiB
GLSL

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 formal 32-bit render object id across
// RGBA. Low-valued ids legitimately have zero alpha, so 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
}
}
}