static const float LUT_SIZE = 33.0; static const float LUT_LAST_INDEX = 32.0; float3 sampleLutCell(float3 index) { float r = floor(index.r + 0.5); float g = floor(index.g + 0.5); float b = floor(index.b + 0.5); float atlasWidth = LUT_SIZE * LUT_SIZE; float2 lutUv; lutUv.x = (r + b * LUT_SIZE + 0.5) / atlasWidth; lutUv.y = (g + 0.5) / LUT_SIZE; return lutTexture.Sample(lutUv).rgb; } float3 applyLut33(float3 color) { float3 lutCoord = saturate(color) * LUT_LAST_INDEX; float3 baseIndex = floor(lutCoord); float3 nextIndex = min(baseIndex + 1.0, LUT_LAST_INDEX); float3 blend = lutCoord - baseIndex; float3 c000 = sampleLutCell(float3(baseIndex.r, baseIndex.g, baseIndex.b)); float3 c100 = sampleLutCell(float3(nextIndex.r, baseIndex.g, baseIndex.b)); float3 c010 = sampleLutCell(float3(baseIndex.r, nextIndex.g, baseIndex.b)); float3 c110 = sampleLutCell(float3(nextIndex.r, nextIndex.g, baseIndex.b)); float3 c001 = sampleLutCell(float3(baseIndex.r, baseIndex.g, nextIndex.b)); float3 c101 = sampleLutCell(float3(nextIndex.r, baseIndex.g, nextIndex.b)); float3 c011 = sampleLutCell(float3(baseIndex.r, nextIndex.g, nextIndex.b)); float3 c111 = sampleLutCell(float3(nextIndex.r, nextIndex.g, nextIndex.b)); float3 c00 = lerp(c000, c100, blend.r); float3 c10 = lerp(c010, c110, blend.r); float3 c01 = lerp(c001, c101, blend.r); float3 c11 = lerp(c011, c111, blend.r); float3 c0 = lerp(c00, c10, blend.g); float3 c1 = lerp(c01, c11, blend.g); return lerp(c0, c1, blend.b); } float4 shadeVideo(ShaderContext context) { float4 source = context.sourceColor; float3 inputColor = source.rgb * pow(2.0, preExposure); if (clampInput) inputColor = saturate(inputColor); float3 lutColor = applyLut33(inputColor); float3 graded = lerp(inputColor, lutColor, lutStrength); graded = (graded - 0.5) * postContrast + 0.5; return float4(saturate(graded), source.a); }