2019-12-27 09:26:59 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2017-07-26 10:00:02 -04:00
2020-10-22 19:19:16 -04:00
#pragma once
2018-09-11 14:44:10 -04:00
2017-07-26 10:00:02 -04:00
#include "Common.ush"
2018-09-11 14:44:10 -04:00
#include "Random.ush"
2021-07-27 22:07:02 -04:00
#include "GammaCorrectionCommon.ush"
2021-06-21 15:20:42 -04:00
#ifndef VT_DISABLE_VIEW_UNIFORM_BUFFER
#define VT_DISABLE_VIEW_UNIFORM_BUFFER 0
2021-05-03 14:07:53 -04:00
#endif
2019-06-11 18:27:07 -04:00
#ifndef NUM_VIRTUALTEXTURE_SAMPLES
#error NUM_VIRTUALTEXTURE_SAMPLES should be set to the number of VT samples that will be done before including this file (exluding any lightmap samples)
#endif
2017-07-26 10:00:02 -04:00
2019-06-11 18:27:07 -04:00
#ifndef LIGHTMAP_VT_ENABLED
#define LIGHTMAP_VT_ENABLED 0
#endif
2020-10-22 19:19:16 -04:00
/** Struct used to store feedback information in. */
2019-06-11 18:27:07 -04:00
struct FVirtualTextureFeedbackParams
{
uint Request; // The tile to request for this pixel
#if (NUM_VIRTUALTEXTURE_SAMPLES + LIGHTMAP_VT_ENABLED) > 1
uint RequestId; // The id of the VT texture sample this pixel will generate feedback for
#endif
};
2020-10-22 19:19:16 -04:00
/** Initializes the FVirtualTextureFeedbackParams for the pixel shader. */
2019-06-11 18:27:07 -04:00
void InitializeVirtualTextureFeedback(in out FVirtualTextureFeedbackParams Params, uint2 SvPosition, uint FrameNumber)
{
#if (NUM_VIRTUALTEXTURE_SAMPLES + LIGHTMAP_VT_ENABLED) > 1
2021-05-04 17:20:17 -04:00
const uint NumVTSamplesInShader = NUM_VIRTUALTEXTURE_SAMPLES + LIGHTMAP_VT_ENABLED;
2021-09-13 09:57:32 -04:00
const uint2 PixelPos = SvPosition >> View.VirtualTextureFeedbackShift;
2021-05-04 17:20:17 -04:00
const uint FeedbackPos = PixelPos.y * View.VirtualTextureFeedbackStride + PixelPos.x;
Params.RequestId = (View.VirtualTextureFeedbackSampleOffset + FeedbackPos) % NumVTSamplesInShader;
2019-06-11 18:27:07 -04:00
Params.Request = 0xFFFFFFFF;
#else
Params.Request = 0xFFFFFFFF;
#endif
2017-07-26 10:00:02 -04:00
}
2020-10-22 19:19:16 -04:00
/** Store feedback info for a VT request in the passed in FVirtualTextureFeedbackParams. */
2019-06-11 18:27:07 -04:00
void StoreVirtualTextureFeedback(in out FVirtualTextureFeedbackParams Params, uint RequestId, uint Request)
2017-07-26 10:00:02 -04:00
{
2019-06-11 18:27:07 -04:00
#if (NUM_VIRTUALTEXTURE_SAMPLES + LIGHTMAP_VT_ENABLED) > 1
Params.Request = (RequestId == Params.RequestId) ? Request : Params.Request;
#else
Params.Request = Request;
#endif
}
2017-07-26 10:00:02 -04:00
2020-10-22 19:19:16 -04:00
/** This should be called at the end of the pixel shader to write out the gathered VT feedback info to the OutputBuffer. */
2023-02-08 09:27:57 -05:00
void FinalizeVirtualTextureFeedback(in FVirtualTextureFeedbackParams Params, float4 SvPosition, float Opacity, uint FrameNumber, RWStructuredBuffer<uint> OutputBuffer)
2019-06-11 18:27:07 -04:00
{
2021-09-13 09:57:32 -04:00
uint2 PixelTilePos = (uint2)SvPosition.xy & View.VirtualTextureFeedbackMask;
uint PixelTileIndex = (PixelTilePos.y << View.VirtualTextureFeedbackShift) + PixelTilePos.x;
2020-12-11 14:21:20 -04:00
2019-06-11 18:27:07 -04:00
// This code will only run every few pixels...
2020-12-11 14:21:20 -04:00
[branch] if (PixelTileIndex == View.VirtualTextureFeedbackJitterOffset)
2019-06-11 18:27:07 -04:00
{
// TODO use append buffer ?
2021-09-13 09:57:32 -04:00
uint2 PixelPos = (uint2)SvPosition.xy >> View.VirtualTextureFeedbackShift;
2020-12-11 14:21:20 -04:00
uint FeedbackPos = PixelPos.y * View.VirtualTextureFeedbackStride + PixelPos.x;
2019-06-11 18:27:07 -04:00
// When using DBuffer, feedback buffer is blending using decal opacity for opaque objects
// This way we avoid overwriting values already written to feedback buffer while rendering dbuffer decals
2023-03-06 04:20:27 -05:00
#if MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_MODULATE || MATERIAL_SHADINGMODEL_SINGLELAYERWATER || IS_DECAL || (USE_DBUFFER && MATERIAL_DECAL_READ_MASK)
2019-06-11 18:27:07 -04:00
// Stochastic alpha testing
2023-03-06 04:20:27 -05:00
#if MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_MODULATE || MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT || MATERIAL_SHADINGMODEL_SINGLELAYERWATER
2019-06-11 18:27:07 -04:00
// Opacity may not make sense for additive and modulation blending
// Use a value of 0.25f so we have up to 4 layers of VT transparancy
const float Alpha = 0.25f;
#else
2021-08-02 09:39:42 -04:00
2021-09-13 09:57:32 -04:00
#if (IS_DECAL && !IS_DBUFFER_DECAL)
2021-08-02 09:39:42 -04:00
// Even fully opaque gbuffer decals may not completely overwrite data from base pass, so don't allow VT feedback to become fully opaque.
// TODO - it may be possible to allow fully opaque decals with certain blend-mode/state combinations, if we can guarantee they overwrite all gbuffer channels
2021-09-13 09:57:32 -04:00
const float MaxAlpha = 0.5f;
#elif (USE_DBUFFER && MATERIAL_DECAL_READ_MASK && !MATERIALDECALRESPONSEMASK)
// When reading the DBuffer directly (so without a blend alpha available) we don't wan't to allow the base pass to be fully opaque for VT feedback.
const float MaxAlpha = 0.5f;
#else
const float MaxAlpha = 1.0f;
#endif
const float MinAlpha = 0.02f;
const float Alpha = clamp(Opacity, MinAlpha, MaxAlpha);
2021-08-02 09:39:42 -04:00
#endif
2021-09-13 09:57:32 -04:00
const float AlphaThreshold = frac(
PseudoRandom(PixelPos + FrameNumber) + // Random value in 0-1 on feedback tile. Add in framenumber for extra jitter so the pseudorandom pattern changes over time.
SvPosition.w // Add in depth so we pick different thresholds on different depths
);
2019-06-11 18:27:07 -04:00
// Threshold is uniform pseudo random this conditional has a higher probability to succeed for higher apha values
// that means more opaque surfaces get more pixels in the feedback buffer which is what we want
[branch] if (Alpha > AlphaThreshold)
{
2020-01-20 22:16:23 -05:00
OutputBuffer[FeedbackPos] = Params.Request;
2019-06-11 18:27:07 -04:00
}
#else
2020-01-20 22:16:23 -05:00
OutputBuffer[FeedbackPos] = Params.Request;
2019-06-11 18:27:07 -04:00
#endif
}
}
2020-10-22 19:19:16 -04:00
/**
* Address Mode Logic
* Apply before after calculating mip level/derivatives!
*/
#define VTADDRESSMODE_CLAMP 0u
#define VTADDRESSMODE_WRAP 1u
#define VTADDRESSMODE_MIRROR 2u
2019-06-11 18:27:07 -04:00
float ApplyAddressModeMirror(float v)
{
float t = frac(v * 0.5f) * 2.0f;
return 1.0f - abs(t - 1.0f);
}
float ApplyAddressMode(float v, uint AddressMode)
{
// For CLAMP address mode, can't clamp to 1.0f, otherwise 'uint(UV * SizeInPages)' will overflow page table bounds by 1
// Instead, clamp to slightly below 1, this ensures that when rounded down to uint, above value will be at most 'SizeInPages - 1'
// The actual texel we clamp to doesn't matter too much for sampling physical texture, since we have borders around the physical pages
// Just need to make sure we don't clamp too far and chop off valid texels at the edge of texture
const float MaxTextureSize = 65536.0f;
if(AddressMode == VTADDRESSMODE_WRAP) return frac(v);
else if(AddressMode == VTADDRESSMODE_MIRROR) return ApplyAddressModeMirror(v);
else return clamp(v, 0.0f, 1.0f - (1.0f / MaxTextureSize));
}
2020-10-22 19:19:16 -04:00
float2 ApplyAddressMode(float2 UV, uint AddressU, uint AddressV)
{
return float2(ApplyAddressMode(UV.x, AddressU), ApplyAddressMode(UV.y, AddressV));
}
/** Non aniso mip level calculation. */
2019-06-11 18:27:07 -04:00
float MipLevel2D( float2 dUVdx, float2 dUVdy )
{
const float px = dot( dUVdx, dUVdx );
const float py = dot( dUVdy, dUVdy );
return 0.5f * log2( max( px, py ) );
}
2020-10-22 19:19:16 -04:00
/** Aniso mip level calculation. */
2019-06-11 18:27:07 -04:00
float MipLevelAniso2D( float2 dUVdx, float2 dUVdy, const float MaxAnisoLog2 )
{
const float px = dot( dUVdx, dUVdx );
const float py = dot( dUVdy, dUVdy );
const float MinLevel = 0.5f * log2( min( px, py ) );
const float MaxLevel = 0.5f * log2( max( px, py ) );
const float AnisoBias = min( MaxLevel - MinLevel, MaxAnisoLog2 );
const float Level = MaxLevel - AnisoBias;
2017-07-26 10:00:02 -04:00
return Level;
}
2020-10-22 19:19:16 -04:00
/** Unpacked contents of per page table uniforms. */
2019-06-11 18:27:07 -04:00
struct VTPageTableUniform
2017-07-26 10:00:02 -04:00
{
2019-06-11 18:27:07 -04:00
uint XOffsetInPages; // 12
uint YOffsetInPages; // 12
uint MaxLevel; // 4
uint vPageTableMipBias; // 8
uint ShiftedPageTableID; // 4
2020-10-22 19:19:16 -04:00
uint AdaptiveLevelBias; // 4
2017-07-26 10:00:02 -04:00
2019-06-11 18:27:07 -04:00
float2 SizeInPages;
float2 UVScale;
float MaxAnisoLog2;
};
2017-07-26 10:00:02 -04:00
2020-10-22 19:19:16 -04:00
/** Unpack the per page table uniforms. */
2019-06-11 18:27:07 -04:00
VTPageTableUniform VTPageTableUniform_Unpack(uint4 PackedPageTableUniform0, uint4 PackedPageTableUniform1)
{
VTPageTableUniform result;
result.UVScale = asfloat(PackedPageTableUniform0.xy);
result.SizeInPages = asfloat(PackedPageTableUniform0.zw);
result.MaxAnisoLog2 = asfloat(PackedPageTableUniform1.x);
result.XOffsetInPages = PackedPageTableUniform1.y & 0xfff;
result.YOffsetInPages = (PackedPageTableUniform1.y >> 12) & 0xfff;
result.vPageTableMipBias = (PackedPageTableUniform1.y >> 24) & 0xff;
2020-10-22 19:19:16 -04:00
result.MaxLevel = PackedPageTableUniform1.z & 0xf;
result.AdaptiveLevelBias = (PackedPageTableUniform1.z >> 4) & 0xf;
2019-06-11 18:27:07 -04:00
result.ShiftedPageTableID = PackedPageTableUniform1.w;
return result;
2017-07-26 10:00:02 -04:00
}
2020-10-22 19:19:16 -04:00
/** Structure carrying page table read result. Is passed as inout parameter and partially filled by several functions. */
2019-06-11 18:27:07 -04:00
struct VTPageTableResult
2017-07-26 10:00:02 -04:00
{
2019-06-11 18:27:07 -04:00
float2 UV;
float2 dUVdx;
float2 dUVdy;
2023-01-29 23:03:58 -05:00
uint4 PageTableValue[4];
2019-06-11 18:27:07 -04:00
uint PackedRequest;
2023-01-29 23:03:58 -05:00
float MipLevelFrac;
2019-06-11 18:27:07 -04:00
};
2021-12-03 02:44:01 -05:00
uint GetRequestedLevel(VTPageTableResult PageTableResult)
{
uint RequestLevelPlusOne = (PageTableResult.PackedRequest >> 24) & 0xf;
2021-12-03 15:37:09 -05:00
uint RequestLevel = max(RequestLevelPlusOne, 1u) - 1u;
2021-12-03 02:44:01 -05:00
return RequestLevel;
}
uint GetSampledLevel(VTPageTableResult PageTableResult, uint LayerIndex, uint FallbackValue)
{
uint PackedPageTableValue = PageTableResult.PageTableValue[LayerIndex / 4u][LayerIndex & 3u];
// 0 is an empty value in the PageTableResult.
// We must also consider 1 as an empty value since it will appear in the .w channel page table textures with less than 4 channels.
// todo: Inject the number of layers inside VTPageTableResult and avoid the need to special case values here.
// HLSLMaterialTranslator could pass the value to TextureLoadVirtualPageTable through a macro similar to VIRTUALTEXTURE_PAGETABLE_%d.
if (PackedPageTableValue != 0 && PackedPageTableValue != 1)
{
uint SampleLevel = PackedPageTableValue & 0xf;
return SampleLevel;
}
// Return whatever the caller can interpret as empty.
return FallbackValue;
}
2021-06-21 15:20:42 -04:00
float GetStochasticMipNoise(float2 SvPositionXY)
{
#if VT_DISABLE_VIEW_UNIFORM_BUFFER
return 0;
#else
return InterleavedGradientNoise(SvPositionXY, View.StateFrameIndexMod8);
#endif
}
float GetGlobalVirtualTextureMipBias()
{
#if VT_DISABLE_VIEW_UNIFORM_BUFFER
return 0;
#else
return View.GlobalVirtualTextureMipBias;
#endif
}
2020-10-22 19:19:16 -04:00
/** Calculate mip level including stochastic noise. Also stores derivatives to OutResult for use when sampling physical texture. */
int TextureComputeVirtualMipLevel(
in out VTPageTableResult OutResult,
2019-06-11 18:27:07 -04:00
float2 dUVdx, float2 dUVdy, float MipBias,
float2 SvPositionXY,
VTPageTableUniform PageTableUniform)
{
OutResult.dUVdx = dUVdx * PageTableUniform.SizeInPages;
OutResult.dUVdy = dUVdy * PageTableUniform.SizeInPages;
2021-04-02 14:08:28 -04:00
// Always compute mip level using MipLevelAniso2D, even if VIRTUAL_TEXTURE_ANISOTROPIC_FILTERING is disabled
// This way the VT mip level selection will come much closer to HW mip selection, even if we're not sampling the texture using anisotropic filtering
2020-07-06 18:58:26 -04:00
const float ComputedLevel = MipLevelAniso2D(OutResult.dUVdx, OutResult.dUVdy, PageTableUniform.MaxAnisoLog2);
2021-06-21 15:20:42 -04:00
const float GlobalMipBias = GetGlobalVirtualTextureMipBias();
2023-01-29 23:03:58 -05:00
#if VIRTUAL_TEXTURE_MANUAL_TRILINEAR_FILTERING
const float Noise = 0.f;
#else
2021-06-21 15:20:42 -04:00
const float Noise = GetStochasticMipNoise(SvPositionXY);
2023-01-29 23:03:58 -05:00
#endif
2021-05-03 14:07:53 -04:00
2023-01-29 23:03:58 -05:00
const float MipLevel = ComputedLevel + MipBias + GlobalMipBias + Noise;
const float MipLevelFloor = floor(MipLevel);
OutResult.MipLevelFrac = MipLevel - MipLevelFloor;
return (int)MipLevelFloor + int(PageTableUniform.vPageTableMipBias);
2017-07-26 10:00:02 -04:00
}
2020-10-22 19:19:16 -04:00
/** Samples page table indirection and any apply changes to UV, vLevel and PageTable information for adaptive page tables. */
void ApplyAdaptivePageTableUniform(
Texture2D<uint> PageTableIndirection,
in out VTPageTableResult InOutResult,
in out VTPageTableUniform InOutPageTableUniform,
in out float2 UV,
in out int vLevel)
{
if (vLevel < 0)
{
// Requested level not stored in low mips so find the adaptive page table entry for this UV.
float2 AdaptiveGridPos = UV * InOutPageTableUniform.SizeInPages;
int2 AdaptiveGridCoord = (int2)floor(AdaptiveGridPos);
float2 AdaptiveGridUV = frac(AdaptiveGridPos);
uint PackedAdaptiveDesc = PageTableIndirection.Load(int3(AdaptiveGridCoord, 0));
[branch]
if (PackedAdaptiveDesc != 0)
{
// A valid entry was found so apply changes.
InOutPageTableUniform.XOffsetInPages = PackedAdaptiveDesc & 0xfff;
InOutPageTableUniform.YOffsetInPages = (PackedAdaptiveDesc >> 12) & 0xfff;
InOutPageTableUniform.MaxLevel = (PackedAdaptiveDesc >> 24) & 0xf;
2021-02-09 17:38:18 -04:00
InOutPageTableUniform.SizeInPages = ((int) 1) << InOutPageTableUniform.MaxLevel;
2020-10-22 19:19:16 -04:00
vLevel += InOutPageTableUniform.MaxLevel;
InOutResult.dUVdx *= InOutPageTableUniform.SizeInPages;
InOutResult.dUVdy *= InOutPageTableUniform.SizeInPages;
UV = frac(AdaptiveGridPos);
}
}
}
/** Load from page table and store results. Single page table texture version. */
void TextureLoadVirtualPageTableInternal(
in out VTPageTableResult OutResult,
Texture2D<uint4> PageTable0,
VTPageTableUniform PageTableUniform,
float2 UV, int vLevel)
2017-07-26 10:00:02 -04:00
{
2019-06-11 18:27:07 -04:00
OutResult.UV = UV * PageTableUniform.SizeInPages;
2017-07-26 10:00:02 -04:00
2020-10-22 19:19:16 -04:00
const uint vLevelClamped = clamp(vLevel, 0, int(PageTableUniform.MaxLevel));
2023-01-29 23:03:58 -05:00
uint vPageX = (uint(OutResult.UV.x) + PageTableUniform.XOffsetInPages) >> vLevelClamped;
uint vPageY = (uint(OutResult.UV.y) + PageTableUniform.YOffsetInPages) >> vLevelClamped;
2020-10-22 19:19:16 -04:00
OutResult.PageTableValue[0] = PageTable0.Load(int3(vPageX, vPageY, vLevelClamped));
2019-06-11 18:27:07 -04:00
OutResult.PageTableValue[1] = uint4(0u, 0u, 0u, 0u);
2023-01-29 23:03:58 -05:00
#if VIRTUAL_TEXTURE_MANUAL_TRILINEAR_FILTERING
// Second page table for trilinear.
const uint vLevelClamped2 = clamp(vLevel + 1, 0, int(PageTableUniform.MaxLevel));
const uint vPageX2 = (uint(OutResult.UV.x) + PageTableUniform.XOffsetInPages) >> vLevelClamped2;
const uint vPageY2 = (uint(OutResult.UV.y) + PageTableUniform.YOffsetInPages) >> vLevelClamped2;
OutResult.PageTableValue[2] = PageTable0.Load(int3(vPageX2, vPageY2, vLevelClamped2));
OutResult.PageTableValue[3] = uint4(0u, 0u, 0u, 0u);
// Alternate requests to both mip levels
if ((View.FrameNumber & 1u) == 0u)
{
vLevel += 1;
vPageX = vPageX2;
vPageY = vPageY2;
}
#endif
2019-06-11 18:27:07 -04:00
// PageTableID packed in upper 4 bits of 'PackedPageTableUniform', which is the bit position we want it in for PackedRequest as well, just need to mask off extra bits
OutResult.PackedRequest = PageTableUniform.ShiftedPageTableID;
OutResult.PackedRequest |= vPageX;
OutResult.PackedRequest |= vPageY << 12;
2020-10-22 19:19:16 -04:00
// Feedback always encodes vLevel+1, and subtracts 1 on the CPU side.
// This allows the CPU code to know when we requested a negative vLevel which indicates that we don't have sufficient virtual texture resolution.
const uint vLevelPlusOneClamped = clamp(vLevel + 1, 0, int(PageTableUniform.MaxLevel + 1));
OutResult.PackedRequest |= vLevelPlusOneClamped << 24;
2019-06-11 18:27:07 -04:00
}
2020-10-22 19:19:16 -04:00
/** Load from page table and store results. Two page table texture version. */
void TextureLoadVirtualPageTableInternal(
in out VTPageTableResult OutResult,
2019-06-11 18:27:07 -04:00
Texture2D<uint4> PageTable0, Texture2D<uint4> PageTable1,
VTPageTableUniform PageTableUniform,
2020-10-22 19:19:16 -04:00
float2 UV, int vLevel)
2019-06-11 18:27:07 -04:00
{
OutResult.UV = UV * PageTableUniform.SizeInPages;
2020-10-22 19:19:16 -04:00
const uint vLevelClamped = clamp(vLevel, 0, int(PageTableUniform.MaxLevel));
2023-01-29 23:03:58 -05:00
uint vPageX = (uint(OutResult.UV.x) + PageTableUniform.XOffsetInPages) >> vLevelClamped;
uint vPageY = (uint(OutResult.UV.y) + PageTableUniform.YOffsetInPages) >> vLevelClamped;
2020-10-22 19:19:16 -04:00
OutResult.PageTableValue[0] = PageTable0.Load(int3(vPageX, vPageY, vLevelClamped));
OutResult.PageTableValue[1] = PageTable1.Load(int3(vPageX, vPageY, vLevelClamped));
2019-06-11 18:27:07 -04:00
2023-01-29 23:03:58 -05:00
#if VIRTUAL_TEXTURE_MANUAL_TRILINEAR_FILTERING
// Second page table for trilinear.
const uint vLevelClamped2 = clamp(vLevel + 1, 0, int(PageTableUniform.MaxLevel));
const uint vPageX2 = (uint(OutResult.UV.x) + PageTableUniform.XOffsetInPages) >> vLevelClamped2;
const uint vPageY2 = (uint(OutResult.UV.y) + PageTableUniform.YOffsetInPages) >> vLevelClamped2;
OutResult.PageTableValue[2] = PageTable0.Load(int3(vPageX2, vPageY2, vLevelClamped2));
OutResult.PageTableValue[3] = PageTable1.Load(int3(vPageX2, vPageY2, vLevelClamped2));
// Alternate requests to both mip levels
if ((View.FrameNumber & 1u) == 0u)
{
vLevel += 1;
vPageX = vPageX2;
vPageY = vPageY2;
}
#endif
2019-06-11 18:27:07 -04:00
// PageTableID packed in upper 4 bits of 'PackedPageTableUniform', which is the bit position we want it in for PackedRequest as well, just need to mask off extra bits
OutResult.PackedRequest = PageTableUniform.ShiftedPageTableID;
OutResult.PackedRequest |= vPageX;
OutResult.PackedRequest |= vPageY << 12;
2020-10-22 19:19:16 -04:00
// Feedback always encodes vLevel+1, and subtracts 1 on the CPU side.
// This allows the CPU code to know when we requested a negative vLevel which indicates that we don't have sufficient virtual texture resolution.
const uint vLevelPlusOneClamped = clamp(vLevel + 1, 0, int(PageTableUniform.MaxLevel + 1));
OutResult.PackedRequest |= vLevelPlusOneClamped << 24;
2019-06-11 18:27:07 -04:00
}
2020-10-22 19:19:16 -04:00
/**
* Public functions used by the material system to sample virtual textures.
* These boiler plate functions implement the used permutations from the matrix of sampling behaviours:
* - One or two page table textures
* - Sample(Bias)/SampleGrad/SampleLevel
* - Adaptive page table indirection on or off
* - Feedback on or off
* - Anisotropic filtering on or off
*/
// LoadPageTable: 1 Page table
VTPageTableResult TextureLoadVirtualPageTable(
Texture2D<uint4> PageTable0,
2019-06-11 18:27:07 -04:00
VTPageTableUniform PageTableUniform,
2020-10-22 19:19:16 -04:00
float2 UV, uint AddressU, uint AddressV,
MaterialFloat MipBias, float2 SvPositionXY,
2019-08-09 20:27:12 -04:00
uint SampleIndex,
2020-10-22 19:19:16 -04:00
in out FVirtualTextureFeedbackParams Feedback)
2019-08-09 20:27:12 -04:00
{
2020-10-22 19:19:16 -04:00
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = GetGlobalVirtualTextureMipBias();
2020-10-22 19:19:16 -04:00
#if PIXELSHADER
vLevel = TextureComputeVirtualMipLevel(Result, ddx(UV), ddy(UV), MipBias, SvPositionXY, PageTableUniform);
#endif // PIXELSHADER
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
2019-08-09 20:27:12 -04:00
}
2020-10-22 19:19:16 -04:00
// LoadPageTable: 1 Page table, No feedback
VTPageTableResult TextureLoadVirtualPageTable(
Texture2D<uint4> PageTable0,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat MipBias, float2 SvPositionXY)
2019-08-09 20:27:12 -04:00
{
2020-10-22 19:19:16 -04:00
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = GetGlobalVirtualTextureMipBias();
2020-10-22 19:19:16 -04:00
#if PIXELSHADER
vLevel = TextureComputeVirtualMipLevel(Result, ddx(UV), ddy(UV), MipBias, SvPositionXY, PageTableUniform);
#endif // PIXELSHADER
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
return Result;
2019-06-11 18:27:07 -04:00
}
2020-10-22 19:19:16 -04:00
// LoadPageTable: 2 Page tables
VTPageTableResult TextureLoadVirtualPageTable(
Texture2D<uint4> PageTable0, Texture2D<uint4> PageTable1,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat MipBias, float2 SvPositionXY,
2019-08-09 20:27:12 -04:00
uint SampleIndex,
2020-10-22 19:19:16 -04:00
in out FVirtualTextureFeedbackParams Feedback)
{
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = GetGlobalVirtualTextureMipBias();
2020-10-22 19:19:16 -04:00
#if PIXELSHADER
vLevel = TextureComputeVirtualMipLevel(Result, ddx(UV), ddy(UV), MipBias, SvPositionXY, PageTableUniform);
#endif // PIXELSHADER
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTable1, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
}
// LoadPageTable: 2 Page tables, No feedback
VTPageTableResult TextureLoadVirtualPageTable(
Texture2D<uint4> PageTable0, Texture2D<uint4> PageTable1,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat MipBias, float2 SvPositionXY)
{
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = GetGlobalVirtualTextureMipBias();
2020-10-22 19:19:16 -04:00
#if PIXELSHADER
vLevel = TextureComputeVirtualMipLevel(Result, ddx(UV), ddy(UV), MipBias, SvPositionXY, PageTableUniform);
#endif // PIXELSHADER
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTable1, PageTableUniform, UV, vLevel);
return Result;
}
// LoadPageTable: 1 Page table, SampleGrad
VTPageTableResult TextureLoadVirtualPageTableGrad(
Texture2D<uint4> PageTable0,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat2 dUVdx, MaterialFloat2 dUVdy, float2 SvPositionXY,
uint SampleIndex,
in out FVirtualTextureFeedbackParams Feedback)
{
VTPageTableResult Result = (VTPageTableResult)0;
int vLevel = TextureComputeVirtualMipLevel(Result, dUVdx * PageTableUniform.UVScale, dUVdy * PageTableUniform.UVScale, 0, SvPositionXY, PageTableUniform);
UV = UV * PageTableUniform.UVScale;
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
}
// LoadPageTable: 2 Page tables, SampleGrad
VTPageTableResult TextureLoadVirtualPageTableGrad(
Texture2D<uint4> PageTable0, Texture2D<uint4> PageTable1,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat2 dUVdx, MaterialFloat2 dUVdy, float2 SvPositionXY,
uint SampleIndex,
in out FVirtualTextureFeedbackParams Feedback)
{
VTPageTableResult Result = (VTPageTableResult)0.0f;
int vLevel = TextureComputeVirtualMipLevel(Result, dUVdx * PageTableUniform.UVScale, dUVdy * PageTableUniform.UVScale, 0, SvPositionXY, PageTableUniform);
UV = UV * PageTableUniform.UVScale;
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTable1, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
}
2022-01-31 06:40:51 -05:00
// LoadPageTable: 2 Page tables, SampleGrad, no feedback
VTPageTableResult TextureLoadVirtualPageTableGrad(
Texture2D<uint4> PageTable0, Texture2D<uint4> PageTable1,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat2 dUVdx, MaterialFloat2 dUVdy, float2 SvPositionXY)
{
VTPageTableResult Result = (VTPageTableResult)0.0f;
int vLevel = TextureComputeVirtualMipLevel(Result, dUVdx * PageTableUniform.UVScale, dUVdy * PageTableUniform.UVScale, 0, SvPositionXY, PageTableUniform);
UV = UV * PageTableUniform.UVScale;
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTable1, PageTableUniform, UV, vLevel);
return Result;
}
2020-10-22 19:19:16 -04:00
// LoadPageTable: 1 Page table, SampleLevel
VTPageTableResult TextureLoadVirtualPageTableLevel(
Texture2D<uint4> PageTable0,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat Level,
uint SampleIndex,
in out FVirtualTextureFeedbackParams Feedback)
{
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = (int)floor(Level + GetGlobalVirtualTextureMipBias());
2020-10-22 19:19:16 -04:00
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
}
// LoadPageTable: 1 Page table, SampleLevel, No feedback
VTPageTableResult TextureLoadVirtualPageTableLevel(
Texture2D<uint4> PageTable0,
VTPageTableUniform PageTableUniform,
2019-06-11 18:27:07 -04:00
float2 UV, uint AddressU, uint AddressV,
MaterialFloat Level)
{
2020-10-22 19:19:16 -04:00
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = (int)floor(Level + GetGlobalVirtualTextureMipBias());
2020-10-22 19:19:16 -04:00
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
return Result;
2019-06-11 18:27:07 -04:00
}
2020-10-22 19:19:16 -04:00
// LoadPageTable: 2 Page tables, SampleLevel
VTPageTableResult TextureLoadVirtualPageTableLevel(
Texture2D<uint4> PageTable0, Texture2D<uint4> PageTable1,
VTPageTableUniform PageTableUniform,
2019-06-11 18:27:07 -04:00
float2 UV, uint AddressU, uint AddressV,
2020-10-22 19:19:16 -04:00
MaterialFloat Level,
uint SampleIndex,
in out FVirtualTextureFeedbackParams Feedback)
2019-06-11 18:27:07 -04:00
{
2020-10-22 19:19:16 -04:00
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = (int)floor(Level + GetGlobalVirtualTextureMipBias());
2020-10-22 19:19:16 -04:00
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTable1, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
2019-06-11 18:27:07 -04:00
}
2020-10-22 19:19:16 -04:00
// LoadPageTable: 2 Page tables, SampleLevel, No feedback
VTPageTableResult TextureLoadVirtualPageTableLevel(
Texture2D<uint4> PageTable0, Texture2D<uint4> PageTable1,
VTPageTableUniform PageTableUniform,
2019-06-11 18:27:07 -04:00
float2 UV, uint AddressU, uint AddressV,
2020-10-22 19:19:16 -04:00
MaterialFloat Level)
2019-06-11 18:27:07 -04:00
{
2020-10-22 19:19:16 -04:00
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = (int)floor(Level + GetGlobalVirtualTextureMipBias());
2020-10-22 19:19:16 -04:00
UV = ApplyAddressMode(UV, AddressU, AddressV);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTable1, PageTableUniform, UV, vLevel);
return Result;
2019-06-11 18:27:07 -04:00
}
2020-10-22 19:19:16 -04:00
// LoadPageTable: 1 Page table, Adaptive
2020-12-11 14:21:20 -04:00
VTPageTableResult TextureLoadVirtualPageTableAdaptive(
2020-10-22 19:19:16 -04:00
Texture2D<uint4> PageTable0,
Texture2D<uint> PageTableIndirection,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat MipBias, float2 SvPositionXY,
uint SampleIndex,
in out FVirtualTextureFeedbackParams Feedback)
{
VTPageTableResult Result = (VTPageTableResult)0;
UV = UV * PageTableUniform.UVScale;
2021-06-21 15:20:42 -04:00
int vLevel = GetGlobalVirtualTextureMipBias();
2020-10-22 19:19:16 -04:00
#if PIXELSHADER
vLevel = TextureComputeVirtualMipLevel(Result, ddx(UV), ddy(UV), MipBias, SvPositionXY, PageTableUniform);
#endif // PIXELSHADER
UV = ApplyAddressMode(UV, AddressU, AddressV);
ApplyAdaptivePageTableUniform(PageTableIndirection, Result, PageTableUniform, UV, vLevel);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
}
// LoadPageTable: 1 Page table, Adaptive, SampleGrad
2020-12-11 14:21:20 -04:00
VTPageTableResult TextureLoadVirtualPageTableAdaptiveGrad(
2020-10-22 19:19:16 -04:00
Texture2D<uint4> PageTable0,
Texture2D<uint> PageTableIndirection,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat2 dUVdx, MaterialFloat2 dUVdy, float2 SvPositionXY,
uint SampleIndex,
in out FVirtualTextureFeedbackParams Feedback)
{
VTPageTableResult Result = (VTPageTableResult)0;
int vLevel = TextureComputeVirtualMipLevel(Result, dUVdx * PageTableUniform.UVScale, dUVdy * PageTableUniform.UVScale, 0, SvPositionXY, PageTableUniform);
UV = UV * PageTableUniform.UVScale;
UV = ApplyAddressMode(UV, AddressU, AddressV);
ApplyAdaptivePageTableUniform(PageTableIndirection, Result, PageTableUniform, UV, vLevel);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
}
// LoadPageTable: 1 Page table, Adaptive, SampleLevel
2020-12-11 14:21:20 -04:00
VTPageTableResult TextureLoadVirtualPageTableAdaptiveLevel(
2020-10-22 19:19:16 -04:00
Texture2D<uint4> PageTable0,
Texture2D<uint> PageTableIndirection,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat Level,
uint SampleIndex,
in out FVirtualTextureFeedbackParams Feedback)
{
VTPageTableResult Result = (VTPageTableResult)0;
// Level is an index into the full size adaptive VT. AdaptiveLevelBias shifts it relative to the low mips allocated VT.
2021-06-21 15:20:42 -04:00
int vLevel = (int)floor(Level + GetGlobalVirtualTextureMipBias()) - PageTableUniform.AdaptiveLevelBias;
2020-10-22 19:19:16 -04:00
UV = UV * PageTableUniform.UVScale;
UV = ApplyAddressMode(UV, AddressU, AddressV);
ApplyAdaptivePageTableUniform(PageTableIndirection, Result, PageTableUniform, UV, vLevel);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
StoreVirtualTextureFeedback(Feedback, SampleIndex, Result.PackedRequest);
return Result;
}
// LoadPageTable: 1 Page table, Adaptive, SampleLevel, No feedback
2020-12-11 14:21:20 -04:00
VTPageTableResult TextureLoadVirtualPageTableAdaptiveLevel(
2020-10-22 19:19:16 -04:00
Texture2D<uint4> PageTable0,
Texture2D<uint> PageTableIndirection,
VTPageTableUniform PageTableUniform,
float2 UV, uint AddressU, uint AddressV,
MaterialFloat Level)
{
VTPageTableResult Result = (VTPageTableResult)0;
// Level is an index into the full size adaptive VT. AdaptiveLevelBias shifts it relative to the low mips allocated VT.
2021-06-21 15:20:42 -04:00
int vLevel = (int)floor(Level + GetGlobalVirtualTextureMipBias()) - PageTableUniform.AdaptiveLevelBias;
2020-10-22 19:19:16 -04:00
UV = UV * PageTableUniform.UVScale;
UV = ApplyAddressMode(UV, AddressU, AddressV);
ApplyAdaptivePageTableUniform(PageTableIndirection, Result, PageTableUniform, UV, vLevel);
TextureLoadVirtualPageTableInternal(Result, PageTable0, PageTableUniform, UV, vLevel);
return Result;
}
/** Unpacked contents of per physical sample uniform. */
2019-06-11 18:27:07 -04:00
struct VTUniform
{
// Page sizes are scaled by RcpPhysicalTextureSize
float pPageSize;
float vPageSize;
float vPageBorderSize;
2021-12-03 13:43:10 -05:00
bool bPageTableExtraBits;
float4 FallbackValue;
2019-06-11 18:27:07 -04:00
};
2020-10-22 19:19:16 -04:00
/** Unpack the physical sample uniform. */
2019-06-11 18:27:07 -04:00
VTUniform VTUniform_Unpack(uint4 PackedUniform)
{
VTUniform result;
2021-12-03 13:43:10 -05:00
result.pPageSize = abs(asfloat(PackedUniform.w));
2019-06-11 18:27:07 -04:00
result.vPageSize = asfloat(PackedUniform.y);
result.vPageBorderSize = asfloat(PackedUniform.z);
2021-12-03 13:43:10 -05:00
result.bPageTableExtraBits = asfloat(PackedUniform.w) > 0;
result.FallbackValue.b = float((PackedUniform.x >> 0) & 0xFF) * (1.0f / 255.0f);
result.FallbackValue.g = float((PackedUniform.x >> 8) & 0xFF) * (1.0f / 255.0f);
result.FallbackValue.r = float((PackedUniform.x >> 16) & 0xFF) * (1.0f / 255.0f);
result.FallbackValue.a = float((PackedUniform.x >> 24) & 0xFF) * (1.0f / 255.0f);
2019-06-11 18:27:07 -04:00
return result;
}
2021-12-03 13:43:10 -05:00
/** We use 0 to mark an unmapped page table entry. */
bool IsValid(VTPageTableResult PageTableResult, uint LayerIndex)
{
const uint PackedPageTableValue = PageTableResult.PageTableValue[LayerIndex / 4u][LayerIndex & 3u];
return (PackedPageTableValue >> 4) != 0;
}
2020-10-22 19:19:16 -04:00
/** Applies proper scaling to dUVdx/dUVdy in PageTableResult. */
2019-06-11 18:27:07 -04:00
float2 VTComputePhysicalUVs(in out VTPageTableResult PageTableResult, uint LayerIndex, VTUniform Uniform)
{
const uint PackedPageTableValue = PageTableResult.PageTableValue[LayerIndex / 4u][LayerIndex & 3u];
2019-09-03 18:15:22 -04:00
// See packing in PageTableUpdate.usf
const uint vLevel = PackedPageTableValue & 0xf;
2020-07-06 18:58:26 -04:00
const float UVScale = float(4096u >> vLevel) * (1.0f / 4096.0f);
// This will compile to runtime branch, but should in theory be conditional moves selecting 1 of 2 different bitfield extracting instructions
2021-12-03 13:43:10 -05:00
const uint pPageX = Uniform.bPageTableExtraBits ? (PackedPageTableValue >> 4) & 0xff : (PackedPageTableValue >> 4) & 0x3f;
const uint pPageY = Uniform.bPageTableExtraBits ? (PackedPageTableValue >> 12) & 0xff : (PackedPageTableValue >> 10) & 0x3f;
2019-06-11 18:27:07 -04:00
const float2 vPageFrac = frac(PageTableResult.UV * UVScale);
const float2 pUV = float2(pPageX, pPageY) * Uniform.pPageSize + (vPageFrac * Uniform.vPageSize + Uniform.vPageBorderSize);
const float ddxyScale = UVScale * Uniform.vPageSize;
PageTableResult.dUVdx *= ddxyScale;
PageTableResult.dUVdy *= ddxyScale;
2017-07-26 10:00:02 -04:00
return pUV;
2018-09-11 14:44:10 -04:00
}
2023-01-29 23:03:58 -05:00
MaterialFloat4 TextureVirtualSampleAniso(Texture2D Physical, SamplerState PhysicalSampler, float2 pUV, float2 dUVdx, float2 dUVdy)
{
#if VIRTUAL_TEXTURE_ANISOTROPIC_FILTERING
return Physical.SampleGrad(PhysicalSampler, pUV, dUVdx, dUVdy);
#else
// no need for dUVdx/dUVdy unless we have anistropic filtering enabled
return Physical.SampleLevel(PhysicalSampler, pUV, 0.0f);
#endif
}
2020-10-22 19:19:16 -04:00
// SamplePhysicalTexture: Aniso On
2019-06-11 18:27:07 -04:00
MaterialFloat4 TextureVirtualSample(
Texture2D Physical, SamplerState PhysicalSampler,
VTPageTableResult PageTableResult, uint LayerIndex,
VTUniform Uniform)
2018-09-11 14:44:10 -04:00
{
2023-01-29 23:03:58 -05:00
float2 pUV = VTComputePhysicalUVs(PageTableResult, LayerIndex, Uniform);
bool bValid = IsValid(PageTableResult, LayerIndex);
const MaterialFloat4 Sample1 = bValid ? TextureVirtualSampleAniso(Physical, PhysicalSampler, pUV, PageTableResult.dUVdx, PageTableResult.dUVdy) : Uniform.FallbackValue;
2021-12-03 13:43:10 -05:00
2023-01-29 23:03:58 -05:00
#if VIRTUAL_TEXTURE_MANUAL_TRILINEAR_FILTERING
pUV = VTComputePhysicalUVs(PageTableResult, LayerIndex + 8u, Uniform);
bValid = IsValid(PageTableResult, LayerIndex + 8u);
const MaterialFloat4 Sample2 = bValid ? TextureVirtualSampleAniso(Physical, PhysicalSampler, pUV, PageTableResult.dUVdx, PageTableResult.dUVdy) : Sample1;
return lerp(Sample1, Sample2, PageTableResult.MipLevelFrac);
2022-05-18 05:10:13 -04:00
#else
2023-01-29 23:03:58 -05:00
return Sample1;
2022-05-18 05:10:13 -04:00
#endif
2018-09-11 14:44:10 -04:00
}
2020-10-22 19:19:16 -04:00
// SamplePhysicalTexture: Aniso Off
2019-06-11 18:27:07 -04:00
MaterialFloat4 TextureVirtualSampleLevel(
Texture2D Physical, SamplerState PhysicalSampler,
VTPageTableResult PageTableResult, uint LayerIndex,
VTUniform Uniform)
2018-09-11 14:44:10 -04:00
{
2022-05-18 05:10:13 -04:00
const float2 pUV = VTComputePhysicalUVs(PageTableResult, LayerIndex, Uniform);
const bool bValid = IsValid(PageTableResult, LayerIndex);
// No need to apply dUVdx/dUVdy, don't support anisotropic when sampling a specific level
2021-12-03 13:43:10 -05:00
2022-05-18 05:10:13 -04:00
return bValid ? Physical.SampleLevel(PhysicalSampler, pUV, 0.0f) : Uniform.FallbackValue;
2018-09-11 14:44:10 -04:00
}
2019-09-23 17:20:19 -04:00
2022-01-28 11:25:35 -05:00
/** Helper function to convert world space to virtual texture UV space. */
float2 VirtualTextureWorldToUV(in FLWCVector3 WorldPos, in FLWCVector3 O, in float3 U, in float3 V)
2019-06-11 18:27:07 -04:00
{
2022-01-28 11:25:35 -05:00
// After the subtract we can assume that the offset is small enough for float maths.
float3 P = LWCToFloat(LWCSubtract(WorldPos, O));
2020-09-24 00:43:27 -04:00
return float2(dot(P, U), dot(P, V));
2018-09-11 14:44:10 -04:00
}
2022-09-27 06:58:06 -04:00
/** Derivative-aware helper function to convert world space to virtual texture UV space. */
FloatDeriv2 VirtualTextureWorldToUVDeriv(in FLWCVector3Deriv WorldPos, in FLWCVector3 O, in float3 U, in float3 V)
{
FloatDeriv2 Result;
Result.Value = VirtualTextureWorldToUV(WorldPos.Value, O, U, V);
Result.Ddx = float2(dot(WorldPos.Ddx, U), dot(WorldPos.Ddx, V));
Result.Ddy = float2(dot(WorldPos.Ddy, U), dot(WorldPos.Ddy, V));
return Result;
}
2019-09-23 17:20:19 -04:00
/** Unpack color from YCoCg stored in BC3. */
float3 VirtualTextureUnpackBaseColorYCoCg(in float4 PackedValue)
{
float Y = PackedValue.a;
float Scale = 1.f / ((255.f / 8.f) * PackedValue.b + 1.f);
float Co = (PackedValue.r - 128.f / 255.f) * Scale;
float Cg = (PackedValue.g - 128.f / 255.f) * Scale;
return float3(Y + Co - Cg, Y + Cg, Y - Co - Cg);
}
2019-06-11 18:27:07 -04:00
/** Generic normal unpack funtion. */
2019-09-03 18:44:16 -04:00
float3 VirtualTextureUnpackNormal(in float2 PackedXY, in float PackedSignZ)
2019-06-11 18:27:07 -04:00
{
2020-03-17 23:19:30 -04:00
float2 NormalXY = PackedXY * (255.f / 127.f) - 1.f;
2019-09-03 18:44:16 -04:00
float SignZ = PackedSignZ * 2.f - 1.f;
float NormalZ = sqrt(saturate(1.0f - dot(NormalXY, NormalXY))) * SignZ;
return float3(NormalXY, NormalZ);
2019-06-11 18:27:07 -04:00
}
/** Unpack normal from BC3. */
float3 VirtualTextureUnpackNormalBC3(in float4 PackedValue)
{
2019-09-03 18:44:16 -04:00
return VirtualTextureUnpackNormal(PackedValue.ag, 1.f);
2019-06-11 18:27:07 -04:00
}
2019-08-25 17:04:30 -04:00
/** Unpack normal from two BC3 textures. */
float3 VirtualTextureUnpackNormalBC3BC3(in float4 PackedValue0, in float4 PackedValue1)
{
2019-09-03 18:44:16 -04:00
return VirtualTextureUnpackNormal(float2(PackedValue0.a, PackedValue1.a), PackedValue1.b);
2019-08-25 17:04:30 -04:00
}
2019-06-11 18:27:07 -04:00
/** Unpack normal from BC5. */
float3 VirtualTextureUnpackNormalBC5(in float4 PackedValue)
{
2019-09-03 18:44:16 -04:00
return VirtualTextureUnpackNormal(PackedValue.rg, 1.f);
2019-06-11 18:27:07 -04:00
}
2019-09-23 17:20:19 -04:00
/** Unpack normal from BC5 and BC1 textures. */
float3 VirtualTextureUnpackNormalBC5BC1(in float4 PackedValue0, in float4 PackedValue1)
{
return VirtualTextureUnpackNormal(float2(PackedValue0.x, PackedValue0.y), PackedValue1.b);
}
2019-08-09 20:17:44 -04:00
/** Unpack 16 bit height (with some hardcoded range scale/bias). */
2020-03-17 23:19:30 -04:00
float VirtualTextureUnpackHeight(in float4 PackedValue, in float2 UnpackHeightScaleBias)
2019-06-11 18:27:07 -04:00
{
2020-03-17 23:19:30 -04:00
return PackedValue.r * UnpackHeightScaleBias.x + UnpackHeightScaleBias.y;
2019-06-11 18:27:07 -04:00
}
2021-07-27 22:07:02 -04:00
float3 VirtualTextureUnpackNormalBGR565(in float4 PackedValue)
{
// sign of normal z is considered always positive to save channels
return VirtualTextureUnpackNormal(PackedValue.xz, 1.0);
}
/** Unpack SRGB Color */
float3 VirtualTextureUnpackBaseColorSRGB(in float4 PackedValue)
{
return sRGBToLinear(PackedValue.rgb);
}