You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
7.6 KiB
220 lines
7.6 KiB
#include "GraniteShaderLibBase.hlsl" |
|
|
|
#define VtAddressMode_Wrap 0 |
|
#define VtAddressMode_Clamp 1 |
|
#define VtAddressMode_Udim 2 |
|
|
|
#define VtFilter_Anisotropic 0 |
|
|
|
#define VtLevel_Automatic 0 |
|
#define VtLevel_Lod 1 |
|
#define VtLevel_Bias 2 |
|
#define VtLevel_Derivatives 3 |
|
|
|
#define VtUvSpace_Regular 0 |
|
#define VtUvSpace_PreTransformed 1 |
|
|
|
#define VtSampleQuality_Low 0 |
|
#define VtSampleQuality_High 1 |
|
|
|
struct VtInputParameters |
|
{ |
|
float2 uv; |
|
float lodOrOffset; |
|
float2 dx; |
|
float2 dy; |
|
int addressMode; |
|
int filterMode; |
|
int levelMode; |
|
int uvMode; |
|
int sampleQuality; |
|
int enableGlobalMipBias; |
|
}; |
|
|
|
int VirtualTexturingLookup( |
|
in GraniteConstantBuffers grCB, |
|
in GraniteTranslationTexture translationTable, |
|
in VtInputParameters input, |
|
out GraniteLookupData graniteLookupData, |
|
out float4 resolveResult |
|
) |
|
{ |
|
GraniteStreamingTextureConstantBuffer grSTCB = grCB.streamingTextureBuffer; |
|
GraniteTilesetConstantBuffer tsCB = grCB.tilesetBuffer; |
|
|
|
float2 texCoord = input.uv; |
|
float2 dx; |
|
float2 dy; |
|
float mipLevel; //interger |
|
|
|
if (input.levelMode == VtLevel_Automatic) |
|
{ |
|
dx = ddx(texCoord); |
|
dy = ddy(texCoord); |
|
} |
|
else if (input.levelMode == VtLevel_Bias) |
|
{ |
|
// We can't simply add the bias after the mip-calculation since the derivatives |
|
// are also used when sampling the cache so make sure we apply bias by scaling derivatives |
|
if ( input.sampleQuality == VtSampleQuality_High ) |
|
{ |
|
float offsetPow2 = pow(2.0f, input.lodOrOffset); |
|
dx = ddx(texCoord) * offsetPow2; |
|
dy = ddy(texCoord) * offsetPow2; |
|
} |
|
// In low quality we don't care about cache derivatives and will add the bias later |
|
else |
|
{ |
|
dx = ddx(texCoord); |
|
dy = ddy(texCoord); |
|
} |
|
} |
|
else if (input.levelMode == VtLevel_Derivatives) |
|
{ |
|
dx = input.dx; |
|
dy = input.dy; |
|
} |
|
else /*input.levelMode == VtLevel_Lod*/ |
|
{ |
|
//gra_TrilinearOffset ensures we do round-nearest for no-trilinear and |
|
//round-floor for trilinear. |
|
float clampedLevel = clamp(input.lodOrOffset + gra_TrilinearOffset, 0.0f, gra_NumLevels); |
|
mipLevel = floor(clampedLevel); |
|
dx = float2(frac(clampedLevel), 0.0f); // trilinear blend ratio |
|
dy = float2(0.0f,0.0f); |
|
} |
|
|
|
// Transform the derivatives to atlas space if needed |
|
if (input.uvMode == VtUvSpace_Regular && input.levelMode != VtLevel_Lod) |
|
{ |
|
dx = gra_Transform.zw * dx; |
|
dy = gra_Transform.zw * dy; |
|
} |
|
|
|
if (input.levelMode != VtLevel_Lod) |
|
{ |
|
#ifdef VT_GLOBAL_MIP_BIAS_MULTIPLIER |
|
if (input.enableGlobalMipBias) |
|
{ |
|
dx *= VT_GLOBAL_MIP_BIAS_MULTIPLIER; |
|
dy *= VT_GLOBAL_MIP_BIAS_MULTIPLIER; |
|
} |
|
#endif |
|
|
|
mipLevel = GranitePrivate_CalcMiplevelAnisotropic(grCB.tilesetBuffer, grCB.streamingTextureBuffer, dx, dy); |
|
|
|
// Simply add it here derivatives are wrong from this point onwards but not used anymore |
|
if ( input.sampleQuality == VtSampleQuality_Low && input.levelMode == VtLevel_Bias) |
|
{ |
|
mipLevel += input.lodOrOffset; |
|
// GranitePrivate_CalcMiplevelAnisotropic will already clamp between 0 gra_NumLevels |
|
// But we need to do it again here. The alternative is modifying dx,dy before passing to |
|
// GranitePrivate_CalcMiplevelAnisotropic adding a pow2 + 4 fmuls so probably |
|
// the exra clamp is more appropriate here. |
|
mipLevel = clamp(mipLevel, 0.0f, gra_NumLevels); |
|
} |
|
|
|
mipLevel = floor(mipLevel + 0.5f); //round nearest |
|
} |
|
|
|
// Apply clamp/wrap mode if needed and transform into atlas space |
|
// If the user passes in pre-transformed texture coords clamping and wrapping should be handled by the user |
|
if (input.uvMode == VtUvSpace_Regular) |
|
{ |
|
if (input.addressMode == VtAddressMode_Wrap) |
|
{ |
|
texCoord = frac(input.uv); |
|
} |
|
else if (input.addressMode == VtAddressMode_Clamp) |
|
{ |
|
float2 epsilon2 = float2(gra_AssetWidthRcp, gra_AssetHeightRcp); |
|
texCoord = clamp(input.uv, epsilon2, float2(1,1) - epsilon2); |
|
} |
|
else if (input.addressMode == VtAddressMode_Udim) |
|
{ |
|
// not modified (i.e outside of the 0-1 range, atlas transform below will take care of it) |
|
texCoord = input.uv; |
|
} |
|
|
|
texCoord = Granite_Transform(gra_StreamingTextureCB, texCoord); |
|
} |
|
|
|
// calculate resolver data |
|
float2 level0NumTiles = float2(gra_Level0NumTilesX, gra_Level0NumTilesX*gra_NumTilesYScale); |
|
float2 virtualTilesUv = floor(texCoord * level0NumTiles * pow(0.5, mipLevel)); |
|
resolveResult = GranitePrivate_MakeResolveOutput(tsCB, virtualTilesUv, mipLevel); |
|
|
|
float4 translationTableData; |
|
if (input.levelMode != VtLevel_Lod) |
|
{ |
|
// Look up the physical page indexes and the number of pages on the mipmap |
|
// level of the page in the translation texture |
|
// Note: this is equal for both anisotropic and linear sampling |
|
// We could use a sample bias here for 'auto' mip level detection |
|
#if (GRA_LOAD_INSTR==0) |
|
translationTableData = GranitePrivate_SampleLevel_Translation(translationTable, texCoord, mipLevel); |
|
#else |
|
translationTableData = GranitePrivate_Load(translationTable, gra_Int3(virtualTilesUv, mipLevel)); |
|
#endif |
|
} |
|
else |
|
{ |
|
// Look up the physical page indexes and the number of pages on the mipmap |
|
// level of the page in the translation texture |
|
// Note: this is equal for both anisotropic and linear sampling |
|
// We could use a sample bias here for 'auto' mip level detection |
|
#if (GRA_LOAD_INSTR==0) |
|
translationTableData = GranitePrivate_SampleLevel_Translation(translationTable, texCoord, mipLevel); |
|
#else |
|
translationTableData = GranitePrivate_Load(translationTable, gra_Int3(virtualTilesUv, mipLevel)); |
|
#endif |
|
} |
|
|
|
graniteLookupData.translationTableData = translationTableData; |
|
graniteLookupData.textureCoordinates = texCoord; |
|
graniteLookupData.dX = dx; |
|
graniteLookupData.dY = dy; |
|
|
|
return 1; |
|
} |
|
|
|
int VirtualTexturingSample( |
|
in GraniteTilesetConstantBuffer tsCB, |
|
in GraniteLookupData graniteLookupData, |
|
in GraniteCacheTexture cacheTexture, |
|
in int layer, |
|
in int levelMode, |
|
in int quality, |
|
out float4 result) |
|
{ |
|
// Convert from pixels to [0-1] and look up in the physical page texture |
|
float2 deltaScale; |
|
float3 cacheCoord = GranitePrivate_TranslateCoord(tsCB, graniteLookupData.textureCoordinates, graniteLookupData.translationTableData, layer, deltaScale); |
|
|
|
if ( levelMode != VtLevel_Lod ) |
|
{ |
|
if ( quality == VtSampleQuality_Low ) |
|
{ |
|
// This leads to small artefacts at tile borders but is generally not noticable unless the texture |
|
// is greatly magnified |
|
result = GranitePrivate_SampleArray(cacheTexture, cacheCoord); |
|
} |
|
else /* quality == VtSampleQuality_High */ |
|
{ |
|
deltaScale *= gra_LodBiasPow2; |
|
|
|
// Calculate the delta scale this works by first converting the [0-1] texcoord deltas to |
|
// pixel deltas on the current mip level, then dividing by the cache size to convert to [0-1] cache deltas |
|
float2 sampDeltaX = graniteLookupData.dX*deltaScale; |
|
float2 sampDeltaY = graniteLookupData.dY*deltaScale; |
|
|
|
result = GranitePrivate_SampleGradArray(cacheTexture, cacheCoord, sampDeltaX, sampDeltaY); |
|
} |
|
} |
|
else |
|
{ |
|
result = GranitePrivate_SampleLevelArray(cacheTexture, cacheCoord, graniteLookupData.dX.x); |
|
} |
|
|
|
return 1; |
|
}
|
|
|