Files
2026-11-01 23:39:55 +02:00

132 lines
5.8 KiB
HLSL

#if SHADERPASS != SHADERPASS_CUSTOM_UI
#error SHADERPASS_CUSTOM_UI_is_not_correctly_defined
#endif
PackedVaryings uie_custom_vert(Attributes input)
{
appdata_t uieInput = (appdata_t)0;
uieInput.vertex = float4(input.positionOS, 1.0f);
uieInput.color = input.color;
uieInput.uv = input.uv0;
uieInput.packedIds = input.uv4;
uieInput.circle = input.uv5;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, uieInput);
Varyings varyings = (Varyings)0;
// Run the graph vertex stage and write its (possibly modified) element-local Position into the
// vertex; uie_std_vert then applies translation -> bone -> group -> clip. A pass-through graph
// returns input.positionOS, so the output matches the built-in path. uie_build_vertex also
// forwards the custom interpolators into the Varyings.
#if defined(FEATURES_GRAPH_VERTEX)
uieInput.vertex.xyz = uie_build_vertex(input, varyings);
#endif
v2f uieOutput = uie_std_vert(uieInput);
varyings.positionCS = uieOutput.pos;
// Load the element record once and reuse it for opacity and the world position below; uie_std_vert
// loads its own copy internally, but sharing here avoids the extra reads on the custom path.
uint transformId, opacityId;
float2 translation;
uie_load_element_info(uieInput, transformId, opacityId, translation);
// Custom shaders carry the straight tint (vertexColor * dynamicColor, no opacity, no premultiply)
// and the per-element opacity separately; opacity is applied at the end of the fragment. This
// ignores uie_std_vert's baked color, which is for the built-in path only.
varyings.color = uie_element_tint(uieInput.color, uieInput.packedIds);
varyings.opacity = ReadShaderInfo(uie_id_to_texel_opacity(opacityId)).a;
// The element world position is group * bone * (local + translation). uie_local_to_world stops at
// group space, so apply the group->world transform on top.
#ifdef VARYINGS_NEED_POSITION_WS
varyings.positionWS = TransformObjectToWorld(uie_local_to_world(uieInput.vertex, transformId, translation));
#endif
// User UV0-UV3 are forwarded to texCoord0-3 on demand (opt-in via PanelSettings.extraVertexChannels).
// input.uv0 carries layoutUV in .zw — the surface-input builder strips it before exposing uv0 to graphs.
#ifdef VARYINGS_NEED_TEXCOORD0
varyings.texCoord0 = input.uv0;
#endif
#ifdef VARYINGS_NEED_TEXCOORD1
varyings.texCoord1 = input.uv1;
#endif
#ifdef VARYINGS_NEED_TEXCOORD2
varyings.texCoord2 = input.uv2;
#endif
#ifdef VARYINGS_NEED_TEXCOORD3
varyings.texCoord3 = input.uv3;
#endif
// UITK internals relocated to texCoord4-7; texCoord0-3 stay reserved for the user.
varyings.texCoord4 = uieOutput.uvClip;
varyings.texCoord5 = uieOutput.typeTexSettings;
varyings.texCoord6 = float4(uieOutput.textCoreLoc.x, uieOutput.textCoreLoc.y, input.uv0.z, input.uv0.w); // Layout uv in z, w
varyings.texCoord7 = uieOutput.circle;
UNITY_TRANSFER_INSTANCE_ID(input, varyings);
UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(uieOutput, varyings);
PackedVaryings packedOutput = PackVaryings(varyings);
return packedOutput;
}
// The picking/selection ids must round-trip exactly; UIE_FRAG_T can be fixed4/half4, which may not
// preserve them (e.g. _ObjectId above 2048 loses integer precision in half).
#if defined(SCENEPICKINGPASS) || defined(SCENESELECTIONPASS)
#define UIE_CUSTOM_FRAG_T float4
#else
#define UIE_CUSTOM_FRAG_T UIE_FRAG_T
#endif
UIE_CUSTOM_FRAG_T uie_custom_frag(PackedVaryings packedInput) : SV_Target
{
Varyings varyings = UnpackVaryings(packedInput);
SurfaceDescriptionInputs surfaceDescriptionInputs = BuildSurfaceDescriptionInputs(varyings);
// BuildSurfaceDescriptionInputs only populates opacity when a node requires it; force it from the
// carried varying so the Element Color node's Opacity output is correct even on its own.
surfaceDescriptionInputs.opacity = varyings.opacity;
#if !defined(SCENESELECTIONPASS) && !defined(SCENEPICKINGPASS)
SurfaceDescription surfaceDescription = SurfaceDescriptionFunction(surfaceDescriptionInputs);
#endif
// TODO: In the future, we should try to use surfaceDescription.coverage instead of computing coverage outside
// of the branches like we do here.
half renderType = round(surfaceDescriptionInputs.typeTexSettings.x);
half isArc = surfaceDescriptionInputs.typeTexSettings.w;
float2 outer = surfaceDescriptionInputs.circle.xy;
float2 inner = surfaceDescriptionInputs.circle.zw;
float coverage = uie_sg_compute_aa_coverage(renderType, isArc, outer, inner);
coverage *= uie_fragment_clip(surfaceDescriptionInputs.uvClip.zw);
// Clip fragments when coverage is close to 0 (< 1/256 here).
// This will write proper masks values in the stencil buffer.
clip(coverage - 0.003f);
// Selection/picking only need the covered footprint (rect/arc coverage and clipping above);
// the graph's color output is replaced by the id, so SurfaceDescriptionFunction is skipped.
#if defined(SCENESELECTIONPASS)
return float4(_ObjectId, _PassValue, 1, 1);
#elif defined(SCENEPICKINGPASS)
return _SelectionID;
#else
surfaceDescription.Alpha *= coverage;
// Apply the per-element opacity here, at the very end, instead of baking it into the tint in the
// vertex. Authors can disable this (Graph Settings) and apply opacity themselves via the Element
// Opacity node's output. Read the raw varying rather than surfaceDescriptionInputs.opacity:
// the latter is only populated when a node requires it, but this apply always needs it.
#if defined(UITK_AUTOMATIC_OPACITY)
surfaceDescription.Alpha *= varyings.opacity;
#endif
return float4(surfaceDescription.BaseColor, surfaceDescription.Alpha);
#endif // SCENESELECTIONPASS / SCENEPICKINGPASS
}