Files
2025-05-07 15:07:44 +08:00

144 lines
4.8 KiB
HLSL

// Shapes © Freya Holmér - https://twitter.com/FreyaHolmer/
// Website & Documentation - https://acegikmo.com/shapes/
#include "UnityCG.cginc"
#include "../Shapes.cginc"
#pragma target 3.0
UNITY_INSTANCING_BUFFER_START(Props)
PROP_DEF(int, _ScaleMode)
PROP_DEF(half4, _Color)
PROP_DEF(half4, _ColorEnd)
PROP_DEF(float3, _PointStart)
PROP_DEF(float3, _PointEnd)
PROP_DEF(half, _Thickness)
PROP_DEF(int, _ThicknessSpace)
SHAPES_DASH_PROPERTIES
UNITY_INSTANCING_BUFFER_END(Props)
#include "../DashUtils.cginc"
#define IP_dash_coord intp0.x
#define IP_dash_spacePerPeriod intp0.y
#define IP_dash_thicknessPerPeriod intp0.z
#define IP_pxCoverage intp0.w
struct VertexInput {
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct VertexOutput {
float4 pos : SV_POSITION;
half4 intp0 : TEXCOORD0;
#if defined(CAP_SQUARE)
half colorBlend : TEXCOORD1; // needed since we need unclamped color blend value in the frag shader
#else
half4 color : TEXCOORD1;
#endif
UNITY_FOG_COORDS(2)
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
VertexOutput vert(VertexInput v) {
UNITY_SETUP_INSTANCE_ID(v);
VertexOutput o = (VertexOutput)0;
UNITY_TRANSFER_INSTANCE_ID(v, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
float3 a = LocalToWorldPos( PROP(_PointStart) );
float3 b = LocalToWorldPos( PROP(_PointEnd) );
int scaleMode = PROP(_ScaleMode);
half uniformScale = GetUniformScale();
half scaleThickness = scaleMode == SCALE_MODE_UNIFORM ? uniformScale : 1;
half thickness = PROP(_Thickness) * scaleThickness;
int thicknessSpace = PROP(_ThicknessSpace);
half lineLength;
half3 right;
half3 normal;
half3 forward;
GetDirMag(b - a, /*out*/ forward, /*out*/ lineLength);
float side = saturate( v.vertex.z );
float3 vertOrigin = side > 0.5 ? b : a;
half3 camForward = -DirectionToNearPlanePos(vertOrigin);
half3 camLineNormal;
if( lineLength < 0.0001 ){ // degenerate case (start == end)
right = CAM_RIGHT;
normal = CAM_UP;
forward = CAM_FORWARD;
camLineNormal = normal;
} else {
bool prettyVertical = abs(forward.y) >= 0.99;
half3 upRef = prettyVertical ? half3(1,0,0) : half3(0,1,0);
normal = normalize(cross(upRef,forward));
right = cross( normal, forward );
camLineNormal = normalize(cross(camForward, forward));
}
LineWidthData widthData = GetScreenSpaceWidthDataSimple( vertOrigin, camLineNormal, thickness, thicknessSpace );
o.IP_pxCoverage = widthData.thicknessPixelsTarget;
float radius = widthData.thicknessMeters * 0.5;
half3 localOffset = v.vertex - half3( 0, 0, saturate( v.vertex.z ) ); // if z >= 1 then subtract height (z) by 1 to make it a spherical offset
localOffset *= radius;
half3 vertPos = vertOrigin + localOffset.x * right + localOffset.y * normal;
#ifdef CAP_ROUND
vertPos += localOffset.z * forward;
#elif defined(CAP_SQUARE)
vertPos += (side*2-1) * forward * radius;
#endif
#if defined(CAP_SQUARE)
half k = 2 * radius / lineLength + 1;
half m = -radius / lineLength;
o.colorBlend = k * side + m;
#else
half4 colorStart = PROP(_Color);
half4 colorEnd = PROP(_ColorEnd); // todo: make the gradient thing be a thing
o.color = lerp( colorStart, colorEnd, side );
#endif
// dashes
if( IsDashed() ) {
float projDist = dot( forward, vertPos - a ); // distance along line
DashConfig dash = GetDashConfig(uniformScale);
DashCoordinates dashCoords = GetDashCoordinates( dash, projDist, lineLength, widthData.thicknessMeters, widthData.pxPerMeter );
o.IP_dash_coord = dashCoords.coord;
o.IP_dash_spacePerPeriod = dashCoords.spacePerPeriod;
o.IP_dash_thicknessPerPeriod = dashCoords.thicknessPerPeriod;
}
o.pos = WorldToClipPos( vertPos.xyz );
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
FRAG_OUTPUT_V4 frag( VertexOutput i ) : SV_Target {
UNITY_SETUP_INSTANCE_ID(i);
#if defined(CAP_SQUARE)
// interpolation of colors is done here because we need to clamp the color blend value in the frag shader
// due to that being calculated in the vert shader, but the 0 and 1 crossings are offset from the vert
// todo: use a proper cylinder mesh with extra verts for 0 and 1 crossings
half4 colorStart = PROP(_Color);
half4 colorEnd = PROP(_ColorEnd); // todo: make the gradient thing be a thing
half4 shape_color = lerp( colorStart, colorEnd, saturate(i.colorBlend) );
#else
half4 shape_color = i.color;
#endif
half shape_mask = 1;
DashCoordinates dashCoords;
dashCoords.coord = i.IP_dash_coord;
dashCoords.spacePerPeriod = i.IP_dash_spacePerPeriod;
dashCoords.thicknessPerPeriod = i.IP_dash_thicknessPerPeriod;
ApplyDashMask( /*inout*/ shape_mask, dashCoords, 0, DASH_TYPE_BASIC, 0 );
shape_mask *= saturate(i.IP_pxCoverage);
return SHAPES_OUTPUT( shape_color, shape_mask, i );
}