Opacity); } float4 applyHSLAndOpacityShift(const float3 hslShift, const float opacityShift, const float4 colorUnpremul) { float3 rgb = colorUnpremul.rgb; float y = dot(rgb, float3(0.299, 0.587, 0.114)); float i = dot(rgb, float3(0.596, -0.275, -0.321)); float q = dot(rgb, float3(0.212, -0.523, 0.311)); // When colorUnpremul.rgb is pure black, (y, i, q) == (0, 0, 0), and // atan(0, 0) is undefined. To avoid that, we set hueRadians to 0 in that // case. Generally we aim to avoid branching in the shader for // performance, but in this case the branching is on a uniform value, // which is not an issue for performance like branching on a vertex // attribute value is. float hueRadians = colorUnpremul.rgb == float3(0, 0, 0) ? 0 : atan(q, i); float chroma = sqrt(i * i + q * q); hueRadians -= hslShift.x * radians(360); chroma *= (hslShift.y + 1); y += hslShift.z; i = chroma * cos(hueRadians); q = chroma * sin(hueRadians); float3 yiq = float3(y, i, q); rgb = float3(dot(yiq, float3(1, 0.956, 0.621)), dot(yiq, float3(1, -0.272, -0.647)), dot(yiq, float3(1, -1.107, 1.704))); return float4(rgb, applyOpacityShift(opacityShift, colorUnpremul.a)); } float2 decodeMargins(const float2 labels) { return (4.0 / 126.0) * max(abs(labels) - float2(1.0), float2(0.0)); } float2 calculateAntialiasingAndPositionOutset( const float3 sideDerivativeAndLabel, const float3 forwardDerivativeAndLabel, const mat2 objectToCanvasLinearComponent, out float2 pixelsPerDimension, out float4 normalizedToEdgeLRFB, out float4 outsetPixelsLRFB) { float2 sideDerivative = sideDerivativeAndLabel.xy; float2 forwardDerivative = forwardDerivativeAndLabel.xy; pixelsPerDimension = abs(matrixDeterminant(objectToCanvasLinearComponent)) * float2(dot(sideDerivative, sideDerivative), dot(forwardDerivative, forwardDerivative)) / max(float2(0.000001), float2(length(objectToCanvasLinearComponent * orthogonal(sideDerivative)), length(objectToCanvasLinearComponent * orthogonal(forwardDerivative)))); float2 labels = float2(sideDerivativeAndLabel.z, forwardDerivativeAndLabel.z); normalizedToEdgeLRFB = float4(labels.x > -0.005, // distance to left labels.x < 0.005, // right labels.y > -0.005, // front labels.y < 0.005); // back float pixelOutsetTarget = targetAntialiasingPixelOutset(pixelsPerDimension.x); float2 outsetTargets = float2(pixelOutsetTarget) / pixelsPerDimension; float2 outsets = min(outsetTargets, decodeMargins(labels)); outsets.x = mix(outsetTargets.x, outsets.x, saturate(4.0 * pixelsPerDimension.x - 1.0)); float4 fromEdge = float4(1.0) - normalizedToEdgeLRFB; outsetPixelsLRFB = pixelOutsetTarget * fromEdge * (outsets / outsetTargets).xxyy; float2 sideOutset = sign(labels.x) * outsets.x * sideDerivative; float2 forwardOutset = sign(labels.y) * outsets.y * forwardDerivative; float commonForwardMagnitude = saturate(dot(sideOutset, forwardOutset) / max(0.000001, dot(forwardOutset, forwardOutset))); return sideOutset + (1.0 - commonForwardMagnitude) * forwardOutset; } float2 calculateWindingTextureUv( const float2 surfaceUv, const float particleAnimationOffset, const float textureAnimationProgress, const int numTextureAnimationFrames, const int numTextureAnimationRows, const int numTextureAnimationColumns) { // Progress overshooting 1 is handled by wrapping around to 0. Effectively // progress is mod 1. float progress = fract(textureAnimationProgress + particleAnimationOffset); float numFrames = float(numTextureAnimationFrames); float numRows = float(numTextureAnimationRows); float numColumns = float(numTextureAnimationColumns); float frameIndex = floor(progress * numFrames); float frameRowFractional = frameIndex / numColumns; float frameRow = floor(frameRowFractional); float frameColumn = floor(fract(frameRowFractional) * numColumns); return vec2((surfaceUv.x + frameColumn) / numColumns, (surfaceUv.y + frameRow) / numRows); } float2 unpackFloat2PackedIntoFloat(const float4 unpackingTransform, const float packedValue) { float2 unpacked = float2(floor(packedValue / 4096.0), fract(packedValue / 4096.0) * 4096.0); return unpackingTransform.yw * unpacked + unpackingTransform.xz; } float2 unpackFloat2PackedIntoUByte3(const float4 unpackingTransform, const half3 packedValue) { float mixedXY = 15.9375 * float(packedValue.y); float2 unpacked = float2(4080.0 * float(packedValue.x) + floor(mixedXY), 4096.0 * fract(mixedXY) + 255.0 * float(packedValue.z)); return unpackingTransform.yw * unpacked + unpackingTransform.xz; } float3 unpackPositionAndOpacityShift(const float4 unpackingTransform, const float2 packedValue) { return float3( unpackFloat2PackedIntoFloat(unpackingTransform, packedValue.x), packedValue.y); } float3 unpackPositionAndOpacityShift(const float4 unpackingTransform, const half4 packedValue) { return float3( unpackFloat2PackedIntoUByte3(unpackingTransform, packedValue.xyz), (255.0 / 127.0) * packedValue.w - 1.0); } float3 unpackDerivativeAndLabel(const float4 unusedUnpackingTransform, const float3 unpackedValue) { return unpackedValue; } float3 unpackDerivativeAndLabel(const float4 unpackingTransform, const half4 packedValue) { return float3( unpackFloat2PackedIntoUByte3(unpackingTransform, packedValue.xyz), 255.0 * packedValue.w - 128.0); } float3 unpackHSLColorShift(const float3 unpackedValue) { return unpackedValue; } float3 unpackHSLColorShift(const half4 packedValue) { float mixedXY = 3.984375 * float(packedValue.y); float mixedYZ = 15.9375 * float(packedValue.z); return float3(1020.0 * float(packedValue.x) + floor(mixedXY), 1024.0 * fract(mixedXY) + floor(mixedYZ), 1024.0 * fract(mixedYZ) + 63.75 * float(packedValue.w)) / 511.0 - float3(1.0); } float2 unpackSurfaceUv(const float2 unpackedValue) { return unpackedValue; } float2 unpackSurfaceUv(const half3 packedValue) { float mixedUV = 15.9375 * float(packedValue.y); return float2( (4080.0 * float(packedValue.x) + floor(mixedUV)) / 4095.0, (4096.0 * fract(mixedUV) + 255.0 * float(packedValue.z)) / 4095.0); } float2 unpackSurfaceUv(const half4 packedValue) { float mixedXY = 15.9375 * float(packedValue.y); return float2((4080.0 * float(packedValue.x) + floor(mixedXY)) / 4095.0, (1048576.0 * fract(mixedXY) + 65280.0 * float(packedValue.z) + 255.0 * float(packedValue.w)) / 1048575.0); } float unpackAnimationOffset(const float unpackedValue) { return unpackedValue; } float unpackAnimationOffset(const half packedValue) { return 255.0 * float(packedValue) / 256.0; }