754 lines
35 KiB
HLSL
754 lines
35 KiB
HLSL
/*
|
|
========================================================================
|
|
Copyright (c) Afzaal. All rights reserved.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
========================================================================
|
|
|
|
GitHub : https://github.com/umar-afzaal/LumeniteFX
|
|
Discord : https://discord.gg/deXJrW2dx6
|
|
|
|
|
|
Filename : lumenite_Kernel.fx
|
|
Version : 2026.07.28
|
|
Author : Afzaal (Kaidō)
|
|
Description: Pre-effect for various LumeniteFX shaders.
|
|
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
|
|
|
|
========================================================================
|
|
*/
|
|
|
|
/*------------------.
|
|
| :: DEFINITIONS :: |
|
|
'------------------*/
|
|
#define FOV 60.0
|
|
#define NEAR_PLANE 0.01
|
|
|
|
#ifndef IMAGE_SPACE
|
|
#define IMAGE_SPACE 0
|
|
#endif
|
|
|
|
#ifndef DEBUG_KERNEL
|
|
#define DEBUG_KERNEL 0
|
|
#endif
|
|
|
|
/*--------------.
|
|
| :: HEADERS :: |
|
|
'--------------*/
|
|
#include "ReShade.fxh"
|
|
#if DEBUG_KERNEL
|
|
#include "DrawText.fxh"
|
|
#endif
|
|
#include "./include/lumenite_Projections.fxh"
|
|
#include "./include/lumenite_Helpers.fxh"
|
|
#include "./include/lumenite_Compute.fxh"
|
|
|
|
/*---------------.
|
|
| :: UNIFORMS :: |
|
|
'---------------*/
|
|
#if DEBUG_KERNEL
|
|
uniform int DEBUG_VIEW <
|
|
ui_type = "combo";
|
|
ui_items = "Split View\0"
|
|
"Normals/Depth\0"
|
|
"Optical Flow\0"
|
|
"Motion Vectors\0"
|
|
"Motion Confidence\0"
|
|
;
|
|
ui_label = "Debug View";
|
|
ui_category = "Kernel";
|
|
> = 0;
|
|
#endif
|
|
|
|
namespace Kernel {
|
|
|
|
/*---------------------.
|
|
| :: RENDER TARGETS :: |
|
|
'---------------------*/
|
|
|
|
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
|
|
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
|
|
|
|
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
|
|
sampler2D sConfidence { Texture = tConfidence; };
|
|
|
|
texture tNormals { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; MipLevels = 4; };
|
|
sampler sNormals { Texture = tNormals; };
|
|
|
|
texture2D tDepth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 4; };
|
|
sampler2D sDepth { Texture = tDepth; };
|
|
|
|
texture2D tCurrLuma { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 8; };
|
|
sampler2D sCurrLuma { Texture = tCurrLuma; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
|
|
texture2D tPrevLuma { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 8; };
|
|
sampler2D sPrevLuma { Texture = tPrevLuma; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
|
|
texture2D tFlow128 { Width = BUFFER_WIDTH/128; Height = BUFFER_HEIGHT/128; Format = RG16F; };
|
|
sampler2D sFlow128 { Texture = tFlow128; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
|
|
texture2D tFlow64A { Width = BUFFER_WIDTH/64; Height = BUFFER_HEIGHT/64; Format = RG16F; };
|
|
sampler2D sFlow64A { Texture = tFlow64A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
texture2D tFlow64B { Width = BUFFER_WIDTH/64; Height = BUFFER_HEIGHT/64; Format = RG16F; };
|
|
sampler2D sFlow64B { Texture = tFlow64B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
|
|
texture2D tFlow32A { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = RG16F; };
|
|
sampler2D sFlow32A { Texture = tFlow32A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
texture2D tFlow32B { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = RG16F; };
|
|
sampler2D sFlow32B { Texture = tFlow32B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
|
|
texture2D tFlow16A { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RG16F; };
|
|
sampler2D sFlow16A { Texture = tFlow16A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
texture2D tFlow16B { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RG16F; };
|
|
sampler2D sFlow16B { Texture = tFlow16B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
|
|
texture2D tFlow8 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
|
|
sampler2D sFlow8 { Texture = tFlow8; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
|
|
|
|
texture2D tPrevFrameFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
|
|
sampler2D sPrevFrameFlow { Texture = tPrevFrameFlow; MagFilter = POINT; MinFilter = POINT; };
|
|
|
|
texture2D tPrevConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
|
|
sampler2D sPrevConfidence { Texture = tPrevConfidence; };
|
|
|
|
/*--------------.
|
|
| :: HELPERS :: |
|
|
'--------------*/
|
|
float3 GetColor(float2 uv)
|
|
{
|
|
return tex2Dlod(ReShade::BackBuffer, float4(uv, 0, 0)).rgb;
|
|
}
|
|
|
|
float3 DepthGradient(float t, float2 uv)
|
|
{
|
|
//grayscale: close=dark, far=bright
|
|
float3 depth = saturate(t).xxx;
|
|
const float ditherBit = 8.0;
|
|
float gridPos = frac(dot(uv, (BUFFER_SCREEN_SIZE * float2(1.0 / 16.0, 10.0 / 36.0)) + 0.25));
|
|
float ditherShift = 0.25 * (1.0 / (pow(2.0, ditherBit) - 1.0));
|
|
float3 ditherShiftRGB = float3(ditherShift, -ditherShift, ditherShift); //subpixel dithering
|
|
ditherShiftRGB = lerp(2.0 * ditherShiftRGB, -2.0 * ditherShiftRGB, gridPos);
|
|
return depth + ditherShiftRGB;
|
|
}
|
|
|
|
float3 MotionToColor(float2 motion)
|
|
{
|
|
float angle = atan2(-motion.y, -motion.x) / 6.283 + 0.5;
|
|
float rawLength = length(motion) / (15.0 * BUFFER_PIXEL_SIZE.x);
|
|
float compressed = rawLength / (1.0 + rawLength * 1.4); //asymptotic squash
|
|
float boosted = pow(compressed, 0.5); //lift shadows
|
|
float magnitude = saturate(lerp(compressed, boosted, saturate(rawLength * 3.0)));
|
|
float3 hsv = float3(angle, 1, magnitude);
|
|
float4 K = float4(1, 2/3.0, 1/3.0, 3);
|
|
float3 p = abs(frac(hsv.xxx + K.xyz) * 6 - K.www);
|
|
return hsv.z * lerp(K.xxx, clamp(p - K.xxx, 0, 1), hsv.y) + 0.1;
|
|
}
|
|
|
|
float SegmentDist(float2 p, float2 a, float2 b) //anti-aliased distance from point p to segment a-b
|
|
{
|
|
float2 pa = p - a;
|
|
float2 ba = b - a;
|
|
float h = saturate(dot(pa, ba) / (dot(ba, ba) + EPSILON));
|
|
return length(pa - ba * h);
|
|
}
|
|
|
|
float4 DrawMotionVectors(float2 uv)
|
|
{
|
|
static const int GATHER = 2; //cell radius searched (5x5); always MAX_LENGTH <= GATHER*GRID_SPACING
|
|
static const float GRID_SPACING = 16.0; //px between grid nodes
|
|
static const float DOT_RADIUS = 2.0; //px radius of node dots
|
|
static const float GRID_OPACITY = 0.20; //0..1 lattice visibility
|
|
static const float3 GRID_TINT = float3(0.55, 0.55, 0.60);
|
|
|
|
static const float SHAFT_THICKNESS = 1.5; //px half-width of shaft (larger)
|
|
static const float HEAD_LENGTH = 6.0; //px length of arrowhead (larger)
|
|
static const float HEAD_HALF_WIDTH = 4.0; //px half-width of head base (larger)
|
|
static const float MIN_LENGTH = 7.0; //px shortest arrow
|
|
static const float MAX_LENGTH = 30.0; //px longest arrow (<= GATHER*GRID_SPACING)
|
|
static const float LENGTH_SCALE = 2.5; //arrow px per motion px (elongation gain)
|
|
static const float AA = 0.9; //px edge softness
|
|
|
|
float3 baseColor = GetColor(uv);
|
|
float2 pixelPos = uv * BUFFER_SCREEN_SIZE;
|
|
|
|
//dotted grid
|
|
float2 g = pixelPos / GRID_SPACING;
|
|
float2 nearest = round(g) * GRID_SPACING; //nearest node centre, px
|
|
float dDot = length(pixelPos - nearest); //px distance to that node
|
|
float gridCov = (1.0 - smoothstep(DOT_RADIUS - AA, DOT_RADIUS + AA, dDot)) * GRID_OPACITY;
|
|
|
|
float bestCov = 0.0;
|
|
float3 bestColor = float3(0.0, 0.0, 0.0);
|
|
|
|
//union of arrows from the (2*GATHER+1)^2 nearest nodes (roots on grid crossings)
|
|
float2 baseNode = round(g);
|
|
[unroll] for (int ny = -GATHER; ny <= GATHER; ny++)
|
|
[unroll] for (int nx = -GATHER; nx <= GATHER; nx++)
|
|
{
|
|
float2 rootPx = (baseNode + float2(nx, ny)) * GRID_SPACING; //node sits on a crossing
|
|
float2 rootUV = rootPx * BUFFER_PIXEL_SIZE;
|
|
|
|
float2 motion = tex2Dlod(sFlow, float4(rootUV, 0, 0)).xy;
|
|
float2 motionPx = motion * BUFFER_SCREEN_SIZE;
|
|
float magPx = length(motionPx);
|
|
bool valid = (magPx >= 0.4) && (tex2Dlod(sDepth, float4(rootUV, 0, 0)).r < 0.999);
|
|
|
|
float len = clamp(magPx * LENGTH_SCALE, MIN_LENGTH, MAX_LENGTH); //elongates with this node's motion
|
|
float2 fwd = -motionPx / (magPx + EPSILON); //negate for forward motion
|
|
float2 tip = rootPx + fwd * len;
|
|
float2 perp = float2(-fwd.y, fwd.x);
|
|
|
|
//shaft
|
|
float2 shaftEnd = rootPx + fwd * max(len - HEAD_LENGTH, 0.0);
|
|
float dShaft = SegmentDist(pixelPos, rootPx, shaftEnd);
|
|
float covShaft = 1.0 - smoothstep(SHAFT_THICKNESS - AA, SHAFT_THICKNESS + AA, dShaft);
|
|
|
|
//head
|
|
float2 toTip = pixelPos - tip;
|
|
float along = dot(toTip, -fwd);
|
|
float side = abs(dot(toTip, perp));
|
|
float halfW = HEAD_HALF_WIDTH * saturate(along / HEAD_LENGTH);
|
|
float covAlong = smoothstep(-AA, AA, along) * (1.0 - smoothstep(HEAD_LENGTH - AA, HEAD_LENGTH + AA, along));
|
|
float covHead = covAlong * (1.0 - smoothstep(halfW - AA, halfW + AA, side));
|
|
|
|
float cov = max(covShaft, covHead) * (valid ? 1.0 : 0.0);
|
|
if (cov > bestCov) { bestCov = cov; bestColor = MotionToColor(motion); }
|
|
}
|
|
|
|
float3 outColor = lerp(baseColor, GRID_TINT, gridCov); //lattice underneath
|
|
outColor = lerp(outColor, bestColor, bestCov); //arrows on top
|
|
return float4(outColor, 1.0);
|
|
}
|
|
|
|
float ZMSAD(sampler2D currLumaSrc, sampler2D prevLumaSrc, float2 posA, float2 posB, float2 texelSize, uint mip)
|
|
{
|
|
static const int2 offsets[9] = {
|
|
int2(0, 3),
|
|
int2(0, 1),
|
|
int2(-3,0), int2(-1,0), int2(0, 0), int2(1,0), int2(3,0),
|
|
int2(0,-1),
|
|
int2(0,-3)
|
|
};
|
|
|
|
//gather samples and calculate the mean for each patch
|
|
float samplesA[9], samplesB[9];
|
|
float meanA = 0.0, meanB = 0.0;
|
|
|
|
[unroll] for(int i = 0; i < 9; i++) {
|
|
float2 offset = float2(offsets[i]) * texelSize;
|
|
samplesA[i] = tex2Dlod(currLumaSrc, float4(posA + offset, 0, mip)).r;
|
|
samplesB[i] = tex2Dlod(prevLumaSrc, float4(posB + offset, 0, mip)).r;
|
|
meanA += samplesA[i];
|
|
meanB += samplesB[i];
|
|
}
|
|
meanA /= 9.0;
|
|
meanB /= 9.0;
|
|
|
|
//SAD on the normalized samples
|
|
float err = 0.0;
|
|
[unroll] for(int i = 0; i < 9; i++)
|
|
err += abs((samplesA[i] - meanA) - (samplesB[i] - meanB));
|
|
|
|
return ((err / 9.0) + EPSILON);
|
|
}
|
|
|
|
float2 Median9(sampler2D flowSrc, float2 uv, float2 texelSize, uint mip)
|
|
{
|
|
float2 v[9];
|
|
int idx = 0;
|
|
[unroll] for(int dy = -1; dy <= 1; dy++) for(int dx = -1; dx <= 1; dx++)
|
|
v[idx++] = tex2Dlod(flowSrc, float4(uv + float2(dx, dy) * texelSize, 0, mip)).xy;
|
|
|
|
//bubble sort ensures the Median lands in v[4], only needs 5 passes
|
|
//indices 4,5,6,7,8 contain the 5 largest items, so v[4] is the median
|
|
[unroll] for(int k = 0; k < 5; k++) for(int i = 0; i < 8 - k; i++) { //checks decrease as right side gets sorted
|
|
float2 a = v[i];
|
|
float2 b = v[i+1];
|
|
v[i] = min(a, b);
|
|
v[i+1] = max(a, b);
|
|
}
|
|
|
|
return v[4];
|
|
}
|
|
|
|
float2 BilateralMedian9(sampler2D flowSrc, float2 uv, float2 texelSize, uint mip)
|
|
{
|
|
static const int2 DENSE_3X3[9] = {
|
|
int2(-1,-1), int2(0,-1), int2(1,-1),
|
|
int2(-1, 0), int2(0, 0), int2(1, 0),
|
|
int2(-1, 1), int2(0, 1), int2(1, 1)
|
|
};
|
|
float lumaC = tex2Dlod(sCurrLuma, float4(uv, 0, mip)).x;
|
|
float lumaW = tex2Dlod(sCurrLuma, float4(uv + float2(-1.0, 0.0) * texelSize, 0, mip)).x;
|
|
float lumaE = tex2Dlod(sCurrLuma, float4(uv + float2( 1.0, 0.0) * texelSize, 0, mip)).x;
|
|
float lumaN = tex2Dlod(sCurrLuma, float4(uv + float2( 0.0,-1.0) * texelSize, 0, mip)).x;
|
|
float lumaS = tex2Dlod(sCurrLuma, float4(uv + float2( 0.0, 1.0) * texelSize, 0, mip)).x;
|
|
//central-difference gradient, wider baseline than quad ddx/ddy, derived from real samples
|
|
float dxLuma = (lumaE - lumaW) * 0.5;
|
|
float dyLuma = (lumaS - lumaN) * 0.5;
|
|
float2 v[9];
|
|
uint validCount = 0;
|
|
[unroll] for (int i = 0; i < 9; i++) {
|
|
int2 off = DENSE_3X3[i];
|
|
float2 sampleUV = uv + float2(off) * texelSize;
|
|
//cardinals + center use sampled luma; diagonals get linear prediction
|
|
float sampleLuma = lumaC; //covers (0,0)
|
|
if (off.x == -1 && off.y == 0) sampleLuma = lumaW;
|
|
else if (off.x == 1 && off.y == 0) sampleLuma = lumaE;
|
|
else if (off.x == 0 && off.y == -1) sampleLuma = lumaN;
|
|
else if (off.x == 0 && off.y == 1) sampleLuma = lumaS;
|
|
else if (off.x != 0 && off.y != 0) sampleLuma = lumaC + float(off.x) * dxLuma + float(off.y) * dyLuma;
|
|
bool isValid = abs(lumaC - sampleLuma) <= 0.05;
|
|
v[i] = isValid ? tex2Dlod(flowSrc, float4(sampleUV, 0, 0)).xy : float2(1e38, 1e38);
|
|
validCount += uint(isValid);
|
|
}
|
|
if(validCount < 3u) return v[4];
|
|
//right-to-left bubble: smallest reaches v[0] per pass; after 5 passes, v[0..4] sorted ascending
|
|
[unroll] for(int k = 0; k < 5; k++) for(int j = 7; j >= k; j--) {
|
|
float2 a = v[j];
|
|
float2 b = v[j+1];
|
|
v[j] = min(a, b);
|
|
v[j+1] = max(a, b);
|
|
}
|
|
uint medianIdx = validCount / 2u;
|
|
float2 result = v[1]; //fallback for validCount == 3 (medianIdx 1)
|
|
if (medianIdx == 2u) result = v[2];
|
|
if (medianIdx == 3u) result = v[3];
|
|
if (medianIdx == 4u) result = v[4];
|
|
return result;
|
|
}
|
|
|
|
float2 ATrousFilter(sampler2D motionSrc, float2 uv, uint dilation, uint mip)
|
|
{
|
|
static const int2 offsets[8] = { int2(-1,-1), int2(0,-1), int2(1,-1),
|
|
int2(-1, 0), int2(1, 0),
|
|
int2(-1, 1), int2(0, 1), int2(1, 1) };
|
|
float centerLuma = tex2Dlod(sCurrLuma, float4(uv, 0, mip)).r;
|
|
#if IMAGE_SPACE == 0
|
|
float centerDepth = tex2Dlod(sDepth, float4(uv, 0, mip)).r;
|
|
#endif
|
|
float2 centerFlow = tex2Dlod(motionSrc, float4(uv, 0, 0)).xy;
|
|
float centerConf = max(tex2Dlod(sConfidence, float4(uv, 0, 0)).r, 0.01); //0.01 floor prevents NaN if conf hits 0
|
|
float2 sum = centerFlow * centerConf;
|
|
float totalWeight = centerConf;
|
|
[unroll] for (int i = 0; i < 8; i++) {
|
|
float2 sampleUV = uv + float2(offsets[i]) * dilation * BUFFER_PIXEL_SIZE * 8.0; //*8 = stride of flow grid
|
|
float2 sampleFlow = tex2Dlod(motionSrc, float4(sampleUV, 0, 0)).xy;
|
|
|
|
float sampleConf = tex2Dlod(sConfidence, float4(sampleUV, 0, 0)).r;
|
|
float confWeight = pow(sampleConf, 3.0);
|
|
|
|
float discontinuityGate;
|
|
#if IMAGE_SPACE == 0
|
|
float sampleDepth = tex2Dlod(sDepth, float4(sampleUV, 0, mip)).r;
|
|
float absDepthDiff = abs(centerDepth - sampleDepth);
|
|
float depthWeight = (absDepthDiff < 0.003) ? 1.0 : 0.0;
|
|
discontinuityGate = depthWeight;
|
|
#else
|
|
float2 flowDeltaPx = (sampleFlow - centerFlow) * BUFFER_SCREEN_SIZE; //measure flow disagreement in full-res px
|
|
float rawMotionGate = exp2(-dot(flowDeltaPx, flowDeltaPx) / (0.01 + EPSILON));
|
|
float motionGate = lerp(1.0, rawMotionGate, saturate(centerConf)); //if center flow is unreliable; relax gate so confident neighbors repair it
|
|
discontinuityGate = motionGate;
|
|
#endif
|
|
|
|
float sampleLuma = tex2Dlod(sCurrLuma, float4(sampleUV, 0, mip)).r;
|
|
float absLumaDiff = abs(centerLuma - sampleLuma);
|
|
float lumaWeight = saturate(1.0 - absLumaDiff * 10.0); //10.0: scale, 4.0: sharpness
|
|
|
|
float weight = confWeight * lumaWeight * discontinuityGate;
|
|
sum += sampleFlow * weight;
|
|
totalWeight += weight;
|
|
}
|
|
return sum / (totalWeight + EPSILON);
|
|
}
|
|
|
|
float2 UpscaleFlow(sampler2D coarseSrc, sampler2D currLumaSrc, sampler2D prevLumaSrc, float2 uv, float2 texelSize, uint mip)
|
|
{
|
|
if(FRAME_COUNT == 0) return float2(0, 0);
|
|
|
|
float2 coarseTexelSize = rcp(float2(tex2Dsize(coarseSrc, 0)));
|
|
//pool candidates for tournament selection. order matters here
|
|
float2 candidates[10];
|
|
candidates[0] = tex2D(coarseSrc, uv).xy ;
|
|
candidates[1] = tex2D(coarseSrc, uv + float2(0, -coarseTexelSize.y)).xy ;
|
|
candidates[2] = tex2D(coarseSrc, uv + float2(0, coarseTexelSize.y)).xy ;
|
|
candidates[3] = tex2D(coarseSrc, uv - float2(coarseTexelSize.x, 0)).xy ;
|
|
candidates[4] = tex2D(coarseSrc, uv + float2(coarseTexelSize.x, 0)).xy ;
|
|
candidates[5] = tex2D(coarseSrc, uv + float2(-coarseTexelSize.x, -coarseTexelSize.y)).xy ;
|
|
candidates[6] = tex2D(coarseSrc, uv + float2( coarseTexelSize.x, -coarseTexelSize.y)).xy ;
|
|
candidates[7] = tex2D(coarseSrc, uv + float2(-coarseTexelSize.x, coarseTexelSize.y)).xy ;
|
|
candidates[8] = tex2D(coarseSrc, uv + float2(coarseTexelSize.x, coarseTexelSize.y)).xy ;
|
|
candidates[9] = tex2D(sPrevFrameFlow, uv).xy;
|
|
|
|
float minCost = 1e6;
|
|
float2 prediction = candidates[0];
|
|
[loop] for (int i = 0; i < 10; i++) {
|
|
float cost = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + candidates[i], texelSize, mip);
|
|
if (cost < minCost) {
|
|
minCost = cost;
|
|
prediction = candidates[i];
|
|
}
|
|
}
|
|
|
|
//refinement with parabolic fitting
|
|
float costLeft = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction - float2(texelSize.x, 0), texelSize, mip);
|
|
float costRight = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction + float2(texelSize.x, 0), texelSize, mip);
|
|
float costDown = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction - float2(0, texelSize.y), texelSize, mip);
|
|
float costUp = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction + float2(0, texelSize.y), texelSize, mip);
|
|
//sub-pixel offset (parabolic fitting)
|
|
float2 subpixelOffset;
|
|
subpixelOffset.x = (costLeft - costRight) / (4.0 * (costLeft + costRight - 2.0 * minCost) + EPSILON); //EPSILON for flat surface handling
|
|
subpixelOffset.y = (costDown - costUp) / (4.0 * (costDown + costUp - 2.0 * minCost) + EPSILON);
|
|
//clamp offset to a reasonable range
|
|
subpixelOffset = clamp(subpixelOffset, -0.5, 0.5);
|
|
|
|
return (prediction+subpixelOffset*texelSize);
|
|
}
|
|
|
|
/*--------------.
|
|
| :: SHADERS :: |
|
|
'--------------*/
|
|
void PS_ReconstructNormals(VSOUT input, out float4 gbuffer : SV_Target0, out float depthC : SV_Target1)
|
|
{
|
|
depthC = GetDepth(input.uv);
|
|
|
|
const float2 offsetX = float2(BUFFER_PIXEL_SIZE.x, 0);
|
|
const float2 offsetY = float2(0, BUFFER_PIXEL_SIZE.y);
|
|
|
|
float3 pC = UVToViewSpace(input.uv, depthC, input);
|
|
float3 pL = UVToViewSpace(input.uv - offsetX, GetDepth(input.uv - offsetX), input);
|
|
float3 pR = UVToViewSpace(input.uv + offsetX, GetDepth(input.uv + offsetX), input);
|
|
float3 pT = UVToViewSpace(input.uv - offsetY, GetDepth(input.uv - offsetY), input);
|
|
float3 pB = UVToViewSpace(input.uv + offsetY, GetDepth(input.uv + offsetY), input);
|
|
|
|
float3 diffX2 = pR - pC;
|
|
float3 diffX1 = pC - pL;
|
|
float3 diffY2 = pB - pC;
|
|
float3 diffY1 = pC - pT;
|
|
|
|
float lenSqX2 = dot(diffX2, diffX2);
|
|
float lenSqX1 = dot(diffX1, diffX1);
|
|
float lenSqY2 = dot(diffY2, diffY2);
|
|
float lenSqY1 = dot(diffY1, diffY1);
|
|
|
|
float3 ddx = lenSqX2 < lenSqX1 ? diffX2 : diffX1;
|
|
float3 ddy = lenSqY2 < lenSqY1 ? diffY2 : diffY1;
|
|
float3 geoNormal = normalize(cross(ddx, ddy));
|
|
gbuffer = float4(geoNormal, depthC);
|
|
}
|
|
|
|
float PS_PackFeatures(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
float3 color = GetColor(uv);
|
|
float luma = dot(color, float3(0.2126, 0.7152, 0.0722));
|
|
return luma * rcp(1.0 + luma);
|
|
}
|
|
|
|
float2 PS_ComputeFlow128(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
if(FRAME_COUNT == 0) return float2(0, 0);
|
|
|
|
static const int SEARCH_RADIUS = 3;
|
|
static const uint mip = 5;
|
|
float2 texelSize = BUFFER_PIXEL_SIZE * exp2(mip);
|
|
|
|
//candidate seeds for the coarsest level for tournament selection
|
|
float2 prevSeed = tex2D(sPrevFrameFlow, uv).xy;
|
|
float2 zeroSeed = float2(0, 0);
|
|
float prevCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + prevSeed, texelSize, mip);
|
|
float zeroCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + zeroSeed, texelSize, mip);
|
|
|
|
float2 seed = (zeroCost < prevCost) ? zeroSeed : prevSeed; //pick better candidate as seed
|
|
float2 bestFlow = seed;
|
|
float minCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv+seed, texelSize, mip);
|
|
//search in a grid AROUND the seed
|
|
for (int y = -SEARCH_RADIUS; y <= SEARCH_RADIUS; ++y) for (int x = -SEARCH_RADIUS; x <= SEARCH_RADIUS; ++x) {
|
|
if (x == 0 && y == 0) continue;
|
|
float2 candidateFlow = seed + float2(x, y) * texelSize;
|
|
float cost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + candidateFlow, texelSize, mip);
|
|
if (cost < minCost) {
|
|
minCost = cost;
|
|
bestFlow = candidateFlow;
|
|
if (minCost < 0.01) //near-perfect match found
|
|
return bestFlow;
|
|
}
|
|
}
|
|
return bestFlow;
|
|
}
|
|
|
|
float2 PS_UpscaleFlow64(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return UpscaleFlow(sFlow128, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*16.0, 4);
|
|
}
|
|
|
|
float2 PS_MedianPass64(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return Median9(sFlow64A, uv, BUFFER_PIXEL_SIZE*64.0, 6);
|
|
}
|
|
|
|
float2 PS_UpscaleFlow32(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return UpscaleFlow(sFlow64B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*8.0, 3);
|
|
}
|
|
|
|
float2 PS_MedianPass32(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return Median9(sFlow32A, uv, BUFFER_PIXEL_SIZE*32.0, 5);
|
|
}
|
|
|
|
float2 PS_UpscaleFlow16(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return UpscaleFlow(sFlow32B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*4.0, 2);
|
|
}
|
|
|
|
float2 PS_MedianPass16(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return Median9(sFlow16A, uv, BUFFER_PIXEL_SIZE*16.0, 4);
|
|
}
|
|
|
|
float2 PS_UpscaleFlow8(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return UpscaleFlow(sFlow16B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*2.0, 1);
|
|
}
|
|
|
|
float2 PS_MedianPass8A(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return BilateralMedian9(sFlow, uv, BUFFER_PIXEL_SIZE*8.0, 3);
|
|
}
|
|
|
|
float2 PS_MedianPass8B(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return BilateralMedian9(sFlow8, uv, BUFFER_PIXEL_SIZE*8.0, 3);
|
|
}
|
|
|
|
float2 PS_ATrousPassA(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target //stride 1
|
|
{
|
|
return ATrousFilter(sFlow, uv, 2, 3);
|
|
}
|
|
|
|
float2 PS_ATrousPassB(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target //stride 2
|
|
{
|
|
float2 flow = ATrousFilter(sFlow8, uv, 4, 1);
|
|
//kill sub-pixel noise
|
|
float flowPixelMag = length(flow / BUFFER_PIXEL_SIZE);
|
|
float gate = saturate(1.0 - pow(1.0 - saturate(saturate(flowPixelMag) - 0.2), 10.0)); //SNAP TO REALITY
|
|
return flow*gate;
|
|
}
|
|
|
|
float PS_Confidence(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
if(FRAME_COUNT == 0) return 0.0; //no confidence
|
|
|
|
float2 flow = tex2D(sFlow, uv).xy;
|
|
float2 prevUV = uv + flow; //warp prev frame forward
|
|
if(IsOOB(prevUV)) return 0.0;
|
|
|
|
//look at local contrast for pattern confidence
|
|
float sumX = 0, sumX2 = 0, sumY = 0, sumY2 = 0;
|
|
float2 lumaTexSize = BUFFER_PIXEL_SIZE * 4.0;
|
|
static const float2 offsets[5] = {
|
|
float2(0, 1),
|
|
float2(-1,0), float2(0, 0), float2(1,0),
|
|
float2(0,-1)
|
|
};
|
|
[unroll] for(int i = 0; i < 5; i++) {
|
|
float valCurr = tex2Dlod(sCurrLuma, float4(uv + offsets[i] * lumaTexSize, 0, 2)).r;
|
|
float valPrev = tex2Dlod(sPrevLuma, float4(prevUV + offsets[i] * lumaTexSize, 0, 2)).r;
|
|
sumX += valCurr; sumX2 += valCurr * valCurr;
|
|
sumY += valPrev; sumY2 += valPrev * valPrev;
|
|
}
|
|
float varCurr = max(0.0, (sumX2 / 5.0) - (sumX / 5.0 * sumX / 5.0));
|
|
float varPrev = max(0.0, (sumY2 / 5.0) - (sumY / 5.0 * sumY / 5.0));
|
|
float patternConf = 1.0 - saturate(abs(sqrt(varCurr) - sqrt(varPrev)) / (sqrt(varCurr) + 0.01));
|
|
|
|
//look at neighborhood for flow consistency
|
|
float flowMagnitude = length(flow);
|
|
float2 flowTexelSize = BUFFER_PIXEL_SIZE * 8.0;
|
|
float2 flowN = tex2Dlod(sFlow, float4(uv + float2(0, -flowTexelSize.y), 0, 0)).xy;
|
|
float2 flowS = tex2Dlod(sFlow, float4(uv + float2(0, flowTexelSize.y), 0, 0)).xy;
|
|
float2 flowE = tex2Dlod(sFlow, float4(uv + float2( flowTexelSize.x, 0), 0, 0)).xy;
|
|
float2 flowW = tex2Dlod(sFlow, float4(uv + float2(-flowTexelSize.x, 0), 0, 0)).xy;
|
|
float2 avgNeighborFlow = (flowN + flowS + flowE + flowW) * 0.25;
|
|
float spatialDiff = distance(flow, avgNeighborFlow);
|
|
float spatialThreshold = flowMagnitude * 0.5 + BUFFER_PIXEL_SIZE.x;
|
|
float spatialConfidence = saturate(1.0 - (spatialDiff / (spatialThreshold + EPSILON)));
|
|
|
|
//motion length penalty
|
|
float subpixelThreshold = length(BUFFER_PIXEL_SIZE);
|
|
float lengthConfidence = (flowMagnitude <= subpixelThreshold) ? 1.0 : rcp((flowMagnitude / subpixelThreshold) * 0.05 + 1.0);
|
|
//float panThreshold = BUFFER_PIXEL_SIZE.x * 30.0;
|
|
//float lengthConfidence = (flowMagnitude <= panThreshold) ? 1.0 : rcp(((flowMagnitude - panThreshold) / panThreshold) * 0.1 + 1.0);
|
|
|
|
//current frame final confidence
|
|
float currentConf = spatialConfidence * lengthConfidence * patternConf;
|
|
|
|
//temporal filter
|
|
float historyConf = tex2D(sPrevConfidence, prevUV).r;
|
|
|
|
//DEPRECATED: linear EMA (a=0.15) 15% new + 85% history every frame
|
|
//unbiased (settles at the true mean), very stable but distrusts a real drop only as slowly as it trusts a rise
|
|
//return lerp(historyConf, currentConf, 0.15); //higher makes it react to changes quickly
|
|
|
|
//Asymmetric EMA; a=0.5 only on a genuine drop (>0.05 below history) fast distrust, else a reasonable a=0.1
|
|
//0.05 deadband keeps calm-region jitter on 0.1; only true occlusion/disocclusion bleeds confidence fast
|
|
float alpha = (currentConf < historyConf - 0.05) ? 0.5 : 0.1;
|
|
return lerp(historyConf, currentConf, alpha);
|
|
}
|
|
|
|
void PS_StoreFlow(float4 pos : SV_Position, float2 uv : TEXCOORD, out float2 flow : SV_Target0, out float confidence : SV_Target1)
|
|
{
|
|
flow = tex2D(sFlow, uv).xy;
|
|
confidence = tex2D(sConfidence, uv).r;
|
|
}
|
|
|
|
float PS_StoreLuma(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
return tex2D(sCurrLuma, uv).r;
|
|
}
|
|
|
|
#if DEBUG_KERNEL
|
|
float4 PS_Debug(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
{
|
|
float3 sceneColor = GetColor(uv);
|
|
switch(DEBUG_VIEW)
|
|
{
|
|
case 0: {
|
|
static const float LINE_PX = 1.5; //divider half-width, px
|
|
static const float3 LINE_TINT = float3(0.0, 0.0, 0.0);
|
|
static const float2 BOX_HALF = float2(0.16, 0.18); //centre inset half-extents, uv
|
|
|
|
float2 pixelPos = uv * BUFFER_SCREEN_SIZE;
|
|
float2 centrePx = BUFFER_SCREEN_SIZE * 0.5;
|
|
float2 boxHalfPx = BOX_HALF * BUFFER_SCREEN_SIZE;
|
|
|
|
//axis-aligned box distance
|
|
float2 dd = abs(pixelPos - centrePx) - boxHalfPx;
|
|
float boxSDF = length(max(dd, 0.0)) + min(max(dd.x, dd.y), 0.0);
|
|
|
|
float3 view;
|
|
if (boxSDF < 0.0)
|
|
{
|
|
float2 boxUV = (uv - (0.5 - BOX_HALF)) / (2.0 * BOX_HALF); //full frame mapped into inset
|
|
view = DrawMotionVectors(boxUV).rgb; //centre: motion vectors
|
|
}
|
|
else
|
|
{
|
|
float2 quadUV = frac(uv * 2.0); //flow/confidence remap to full [0,1] frame
|
|
if (uv.y < 0.5)
|
|
view = (uv.x < 0.5)
|
|
? tex2Dlod(sNormals, float4(uv, 0, 0)).rgb * 0.5 + 0.5 //TL: normals (spatial, raw uv)
|
|
: DepthGradient(tex2Dlod(sDepth, float4(uv, 0, 0)).r, uv); //TR: depth (spatial, raw uv)
|
|
else if (uv.x < 0.5)
|
|
view = MotionToColor(tex2Dlod(sFlow, float4(quadUV, 0, 0)).xy); //BL: optical flow field
|
|
else
|
|
{
|
|
float confidence = tex2Dlod(sConfidence, float4(quadUV, 0, 0)).x; //BR: motion confidence field
|
|
float3 confidenceColor = (confidence < 0.5)
|
|
? lerp(float3(1.0, 0.0, 0.0), float3(1.0, 1.0, 0.0), confidence * 2.0)
|
|
: lerp(float3(1.0, 1.0, 0.0), float3(0.0, 1.0, 0.0), (confidence - 0.5) * 2.0);
|
|
view = lerp(GetColor(quadUV), confidenceColor, 0.9);
|
|
}
|
|
|
|
//black dividers
|
|
float dCross = min(abs(pixelPos.x - centrePx.x), abs(pixelPos.y - centrePx.y));
|
|
view = lerp(view, LINE_TINT, 1.0 - smoothstep(LINE_PX - 0.9, LINE_PX + 0.9, dCross));
|
|
}
|
|
|
|
//centre inset border
|
|
view = lerp(view, LINE_TINT, 1.0 - smoothstep(LINE_PX - 0.9, LINE_PX + 0.9, abs(boxSDF)));
|
|
//window labels
|
|
float2 texcoord = uv; //alias: the DrawText macro declares its own internal 'uv'
|
|
float labelMask = 0.0;
|
|
float labelSize = max(BUFFER_HEIGHT * 0.025, 12.0); //label height, px
|
|
int lblNormals[21] = { __R, __e, __c, __o, __n, __s, __t, __r, __u, __c, __t, __e, __d, __Space, __N, __o, __r, __m, __a, __l, __s };
|
|
int lblDepth[16] = { __L, __i, __n, __e, __a, __r, __i, __z, __e, __d, __Space, __D, __e, __p, __t, __h };
|
|
int lblFlow[10] = { __F, __l, __o, __w, __Space, __F, __i, __e, __l, __d };
|
|
int lblConfidence[16] = { __C, __o, __n, __f, __i, __d, __e, __n, __c, __e, __Space, __F, __i, __e, __l, __d };
|
|
int lblVectors[14] = { __M, __o, __t, __i, __o, __n, __Space, __V, __e, __c, __t, __o, __r, __s };
|
|
|
|
labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.25 - 21.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.03), labelSize, 1.0, texcoord, lblNormals, 21, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //TL white
|
|
labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.75 - 16.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.03), labelSize, 1.0, texcoord, lblDepth, 16, labelMask); view = lerp(view, float3(0.55, 0.85, 1.00), saturate(labelMask)); //TR blue
|
|
labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.25 - 10.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.53), labelSize, 1.0, texcoord, lblFlow, 10, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //BL white
|
|
labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.75 - 16.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.53), labelSize, 1.0, texcoord, lblConfidence, 16, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //BR white
|
|
labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.50 - 14.0 * labelSize * 0.25, BUFFER_HEIGHT * (0.5 - BOX_HALF.y) + 8.0), labelSize, 1.0, texcoord, lblVectors, 14, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //centre white
|
|
|
|
view = lerp(view, float3(1.0, 1.0, 1.0), saturate(labelMask)); //white labels
|
|
return float4(view, 1.0);
|
|
}
|
|
case 1: {
|
|
float4 gbuffer = tex2D(sNormals, uv);
|
|
float3 normal = gbuffer.rgb;
|
|
float depth = gbuffer.a;
|
|
bool isLeftHalf = uv.x < 0.5;
|
|
float4 dbg;
|
|
if (isLeftHalf)
|
|
dbg = float4(normal * 0.5 + 0.5, 1.0); //left: normals
|
|
else
|
|
dbg = float4(DepthGradient(depth, uv), 1.0); //right: depth gradient
|
|
return dbg;
|
|
}
|
|
case 2: return float4(MotionToColor(tex2D(sFlow, uv).xy), 1);
|
|
case 3: return DrawMotionVectors(uv);
|
|
case 4:
|
|
{
|
|
float confidence = tex2D(sConfidence, uv).x;
|
|
float3 confidenceColor;
|
|
if (confidence < 0.5)
|
|
confidenceColor = lerp(float3(1.0, 0.0, 0.0), float3(1.0, 1.0, 0.0), confidence * 2.0);
|
|
else
|
|
confidenceColor = lerp(float3(1.0, 1.0, 0.0), float3(0.0, 1.0, 0.0), (confidence - 0.5) * 2.0);
|
|
return float4(lerp(sceneColor, confidenceColor, 0.9), 1.0);
|
|
}
|
|
default: return float4(sceneColor, 1.0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*----------------.
|
|
| :: TECHNIQUE :: |
|
|
'----------------*/
|
|
technique Lumenite_Kernel <
|
|
ui_label = "LUMENITE: Kernel 2.0";
|
|
ui_tooltip = "Pre-effect for LumeniteFX shaders.";
|
|
>
|
|
{
|
|
//normals
|
|
#if IMAGE_SPACE == 0
|
|
pass { VertexShader = VS; PixelShader = PS_ReconstructNormals; RenderTarget0 = tNormals; RenderTarget1 = tDepth; }
|
|
#endif
|
|
|
|
//optical flow
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_PackFeatures; RenderTarget = tCurrLuma; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_ComputeFlow128; RenderTarget = tFlow128; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow64; RenderTarget = tFlow64A; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass64; RenderTarget = tFlow64B; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow32; RenderTarget = tFlow32A; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass32; RenderTarget = tFlow32B; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow16; RenderTarget = tFlow16A; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass16; RenderTarget = tFlow16B; }
|
|
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow8; RenderTarget = tFlow; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass8A; RenderTarget = tFlow8; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass8B; RenderTarget = tFlow; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_Confidence; RenderTarget = tConfidence; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_ATrousPassA; RenderTarget = tFlow8; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_ATrousPassB; RenderTarget = tFlow; }
|
|
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreFlow; RenderTarget0 = tPrevFrameFlow; RenderTarget1 = tPrevConfidence; }
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreLuma; RenderTarget = tPrevLuma; }
|
|
|
|
//debug views
|
|
#if DEBUG_KERNEL
|
|
pass { VertexShader = PostProcessVS; PixelShader = PS_Debug; }
|
|
#endif
|
|
}
|
|
|
|
}
|