201 lines
6.4 KiB
HLSL
201 lines
6.4 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
TextureSampling.usf: Hold helper to do custom texture sampling.
|
|
=============================================================================*/
|
|
|
|
#pragma once
|
|
|
|
|
|
/** Returns the exact UV coordinate of the center of the closest pixel. */
|
|
float2 SnapToClosestPixelCenterUVEx(float2 SampleUV, float2 TextureSize, float2 TextureInvSize, float MipLevelPow2, float InvMipLevelPow2)
|
|
{
|
|
return (TextureInvSize * InvMipLevelPow2) * (floor((TextureSize * MipLevelPow2) * SampleUV) + 0.5);
|
|
}
|
|
|
|
float2 SnapToClosestPixelCenterUV(float2 SampleUV, float2 TextureSize, float2 TextureInvSize, const uint MipLevel = 0)
|
|
{
|
|
return SnapToClosestPixelCenterUVEx(SampleUV, TextureSize, TextureInvSize, float(1u << MipLevel), rcp(float(1u << MipLevel)));
|
|
}
|
|
|
|
|
|
/** Pixel offsets to use for manual bilinear filtering. */
|
|
static const uint2 BilinearSamplingOffsets2x2[4] =
|
|
{
|
|
int2(0, 0),
|
|
int2(1, 0),
|
|
int2(0, 1),
|
|
int2(1, 1),
|
|
};
|
|
|
|
|
|
/** Holds all information to do bilinear sample manually. */
|
|
struct FBilinearSampleInfos
|
|
{
|
|
// The texture's inverse size.
|
|
float2 TextureInvSize;
|
|
|
|
// Pixel coordinate of the top left sample.
|
|
float2 TopLeftPixelCoord;
|
|
|
|
// Bilinear interpolation to perform on X and Y axis.
|
|
float2 BilinearInterp;
|
|
};
|
|
|
|
|
|
/** Generate all information necessary for doing bilinear texture sampling manually. */
|
|
FBilinearSampleInfos GetBilinearSampleLevelInfosEx(float2 SampleUV, float2 TextureSize, float2 TextureInvSize, float MipLevelPow2, float InvMipLevelPow2)
|
|
{
|
|
FBilinearSampleInfos Infos;
|
|
|
|
float2 HistoryBufferPixelCoord = SampleUV * TextureSize * InvMipLevelPow2;
|
|
|
|
Infos.TextureInvSize = TextureInvSize * MipLevelPow2;
|
|
Infos.TopLeftPixelCoord = floor(HistoryBufferPixelCoord - 0.5);
|
|
Infos.BilinearInterp = frac(HistoryBufferPixelCoord - 0.5);
|
|
return Infos;
|
|
}
|
|
|
|
FBilinearSampleInfos GetBilinearSampleLevelInfos(float2 SampleUV, float2 TextureSize, float2 TextureInvSize, const uint MipLevel = 0)
|
|
{
|
|
return GetBilinearSampleLevelInfosEx(SampleUV, TextureSize, TextureInvSize, float(1u << MipLevel), rcp(float(1u << MipLevel)));
|
|
}
|
|
|
|
/** Return the weight of a sample <SampleId>. */
|
|
float GetSampleWeight(FBilinearSampleInfos SampleInfos, const uint SampleId)
|
|
{
|
|
float BilinearSampleWeights[4] = {
|
|
(1 - SampleInfos.BilinearInterp.x) * (1 - SampleInfos.BilinearInterp.y),
|
|
(SampleInfos.BilinearInterp.x) * (1 - SampleInfos.BilinearInterp.y),
|
|
(1 - SampleInfos.BilinearInterp.x) * (SampleInfos.BilinearInterp.y),
|
|
(SampleInfos.BilinearInterp.x) * (SampleInfos.BilinearInterp.y),
|
|
};
|
|
return BilinearSampleWeights[SampleId];
|
|
}
|
|
|
|
/** Return the uv of the sample <SampleId>. */
|
|
float2 GetSampleUV(FBilinearSampleInfos SampleInfos, const uint SampleId)
|
|
{
|
|
return (SampleInfos.TopLeftPixelCoord + (BilinearSamplingOffsets2x2[SampleId] + 0.5)) * SampleInfos.TextureInvSize;
|
|
}
|
|
|
|
/** Return the pixel coordinate of sample <SampleId>. */
|
|
uint2 GetSamplePixelCoord(FBilinearSampleInfos SampleInfos, const uint SampleId)
|
|
{
|
|
return uint2(int2(SampleInfos.TopLeftPixelCoord)) + BilinearSamplingOffsets2x2[SampleId];
|
|
}
|
|
|
|
/** Bilinearly interpolates the four given samples. */
|
|
float GetBilinearInterpolation(FBilinearSampleInfos SampleInfos, float4 Samples)
|
|
{
|
|
float4 BilinearSampleWeights;
|
|
BilinearSampleWeights[0] = (1 - SampleInfos.BilinearInterp.x) * (1 - SampleInfos.BilinearInterp.y);
|
|
BilinearSampleWeights[1] = (SampleInfos.BilinearInterp.x) * (1 - SampleInfos.BilinearInterp.y);
|
|
BilinearSampleWeights[2] = (1 - SampleInfos.BilinearInterp.x) * (SampleInfos.BilinearInterp.y);
|
|
BilinearSampleWeights[3] = (SampleInfos.BilinearInterp.x) * (SampleInfos.BilinearInterp.y);
|
|
|
|
return dot(Samples, BilinearSampleWeights);
|
|
}
|
|
|
|
|
|
void Bicubic2DCatmullRom(in float2 UV, in float2 Size, in float2 InvSize, out float2 Sample[3], out half2 Weight[3])
|
|
{
|
|
UV *= Size;
|
|
|
|
float2 tc = floor(UV - 0.5) + 0.5;
|
|
half2 f = half2(UV - tc);
|
|
half2 f2 = f * f;
|
|
half2 f3 = f2 * f;
|
|
|
|
half2 w0 = f2 - 0.5 * (f3 + f);
|
|
half2 w1 = 1.5 * f3 - 2.5 * f2 + 1;
|
|
half2 w3 = 0.5 * (f3 - f2);
|
|
half2 w2 = 1 - w0 - w1 - w3;
|
|
|
|
Weight[0] = w0;
|
|
Weight[1] = w1 + w2;
|
|
Weight[2] = w3;
|
|
|
|
Sample[0] = tc - 1;
|
|
Sample[1] = tc + w2 * rcp(Weight[1]);
|
|
Sample[2] = tc + 2;
|
|
|
|
Sample[0] *= InvSize;
|
|
Sample[1] *= InvSize;
|
|
Sample[2] *= InvSize;
|
|
}
|
|
|
|
#define BICUBIC_CATMULL_ROM_SAMPLES 5
|
|
|
|
struct FCatmullRomSamples
|
|
{
|
|
// Constant number of samples (BICUBIC_CATMULL_ROM_SAMPLES)
|
|
uint Count;
|
|
|
|
// Constant sign of the UV direction from main UV sampling location.
|
|
int2 UVDir[BICUBIC_CATMULL_ROM_SAMPLES];
|
|
|
|
// Bilinear sampling UV coordinates of the samples
|
|
float2 UV[BICUBIC_CATMULL_ROM_SAMPLES];
|
|
|
|
// Weights of the samples
|
|
half Weight[BICUBIC_CATMULL_ROM_SAMPLES];
|
|
|
|
// Final multiplier (it is faster to multiply 3 RGB values than reweights the 5 weights)
|
|
float FinalMultiplier;
|
|
};
|
|
|
|
FCatmullRomSamples GetBicubic2DCatmullRomSamples(float2 UV, float2 Size, in float2 InvSize)
|
|
{
|
|
FCatmullRomSamples Samples;
|
|
Samples.Count = BICUBIC_CATMULL_ROM_SAMPLES;
|
|
|
|
half2 Weight[3];
|
|
float2 Sample[3];
|
|
Bicubic2DCatmullRom(UV, Size, InvSize, Sample, Weight);
|
|
|
|
// Optimized by removing corner samples
|
|
Samples.UV[0] = float2(Sample[1].x, Sample[0].y);
|
|
Samples.UV[1] = float2(Sample[0].x, Sample[1].y);
|
|
Samples.UV[2] = float2(Sample[1].x, Sample[1].y);
|
|
Samples.UV[3] = float2(Sample[2].x, Sample[1].y);
|
|
Samples.UV[4] = float2(Sample[1].x, Sample[2].y);
|
|
|
|
Samples.Weight[0] = Weight[1].x * Weight[0].y;
|
|
Samples.Weight[1] = Weight[0].x * Weight[1].y;
|
|
Samples.Weight[2] = Weight[1].x * Weight[1].y;
|
|
Samples.Weight[3] = Weight[2].x * Weight[1].y;
|
|
Samples.Weight[4] = Weight[1].x * Weight[2].y;
|
|
|
|
Samples.UVDir[0] = int2(0, -1);
|
|
Samples.UVDir[1] = int2(-1, 0);
|
|
Samples.UVDir[2] = int2(0, 0);
|
|
Samples.UVDir[3] = int2(1, 0);
|
|
Samples.UVDir[4] = int2(0, 1);
|
|
|
|
// Reweight after removing the corners
|
|
float CornerWeights;
|
|
CornerWeights = Samples.Weight[0];
|
|
CornerWeights += Samples.Weight[1];
|
|
CornerWeights += Samples.Weight[2];
|
|
CornerWeights += Samples.Weight[3];
|
|
CornerWeights += Samples.Weight[4];
|
|
Samples.FinalMultiplier = 1 / CornerWeights;
|
|
|
|
return Samples;
|
|
}
|
|
|
|
MaterialFloat4 Texture2DSampleBicubic(Texture2D Tex, SamplerState Sampler, float2 UV, float2 Size, in float2 InvSize)
|
|
{
|
|
FCatmullRomSamples Samples = GetBicubic2DCatmullRomSamples(UV, Size, InvSize);
|
|
|
|
float4 OutColor = 0;
|
|
for (uint i = 0; i < Samples.Count; i++)
|
|
{
|
|
OutColor += Tex.SampleLevel(Sampler, Samples.UV[i], 0) * Samples.Weight[i];
|
|
}
|
|
OutColor *= Samples.FinalMultiplier;
|
|
|
|
return OutColor;
|
|
}
|