// Copyright Epic Games, Inc. All Rights Reserved. // // Shader header file providing helper functions to fetch texels in a tiled raster // #ifndef TILED_FETCH_USH #define TILED_FETCH_USH #include "SamplerStates.ush" // Declare an array of textures which are the 9 tiles defining... // a center 'main' tile, and its 8 possible neighbor tiles: // // Texcoord.x +--- 0 ----- 1/0 ---- 1/0 ----- 1 --> // Texcoord.y | : : : : // | : : : : + TileY // 0 -- +--------+--------+--------+ | // | | | | // | | NW | North | NE | -1 // | | | | // 1/0 - +--------+--------+--------+ | // | | | | // | | West | Main | East | 0 // | | | | // 1/0 - +--------+--------+--------+ | // | | | | // | | SW | South | SE | +1 // | | | | // 1 -- +--------+--------+--------+ | // | V TileY // V // --- -1 ------ 0 ----- +1 --> TileX // // Initialized from cpu with an array of textures in the following layout: // Texture2D tiles[9] = { NW, W, SW, N, Main, S, NE, E, SE } // #define FetchLinear(tex, _uv) \ tex.Sample(SamplerStates_Linear_Clamp, _uv) #define Fetch(tex, _uv) \ tex.Sample(SamplerStates_Clamp, _uv) #define Tile(T, I) T##_##I #define Declare_TiledArray(T) \ Texture2D Tile(T, 0); \ Texture2D Tile(T, 1); \ Texture2D Tile(T, 2); \ Texture2D Tile(T, 3); \ Texture2D Tile(T, 4); \ Texture2D Tile(T, 5); \ Texture2D Tile(T, 6); \ Texture2D Tile(T, 7); \ Texture2D Tile(T, 8); #define Declare_FetchTile(T) \ float4 FetchTile_##T(float2 tile, float2 uv) \ { \ int tileIdx = (tile.x + 1) * 3 + (tile.y + 1); \ \ if (tileIdx == 1) return Fetch(Tile(T, 1), uv); \ if (tileIdx == 7) return Fetch(Tile(T, 7), uv); ; \ if (tileIdx == 3) return Fetch(Tile(T, 3), uv); \ if (tileIdx == 5) return Fetch(Tile(T, 5), uv); \ \ if (tileIdx == 0) return Fetch(Tile(T, 0), uv); \ if (tileIdx == 2) return Fetch(Tile(T, 2), uv); \ if (tileIdx == 6) return Fetch(Tile(T, 6), uv); \ if (tileIdx == 8) return Fetch(Tile(T, 8), uv); \ \ return Fetch(Tile(T, 4), uv); \ } /* This should be the version we use, but we cannot include the comments in the macro... #define Declare_FetchTiled(T) \ float4 FetchTiled_##T(float2 uv) \ { \ // Evaluate the tiles and quad of samples required for this fetch \ float2 tileSize; \ Tile(T,4).GetDimensions(tileSize.x, tileSize.y); \ float2 invTileSize = 1.0 / tileSize; \ float2 tileQuad = 0; \ float tileQuadCase = EvalTileQuadAt(uv, invTileSize, tileQuad); \ \ float2 texelPos = uv * tileSize; \ float2 texelPointPos = floor(texelPos); \ float2 texelPointUV = (texelPointPos + 0.5) * invTileSize; // transform the texel int pos to the actual uv coord \ \ // if only need the main tile, then we are done, just return the fetched value \ if (tileQuadCase == 0) \ return FetchLinear(Tile(T,4), uv); \ \ // else, need to gather texels from different tiles and filter \ \ // First the texel at the point uv in main tile \ float4 texel = Fetch(Tile(T,4), texelPointUV); \ \ // we ll need the position of the fetch in the quad of the 4 texels to do proper filtering \ float2 quadPos = (texelPos - texelPointPos) - 0.5; \ \ // Quad pos is signed properly in the uv space here so we can compute the uv offset for 3 other samples \ float2 uvOffsetXY = sign(quadPos) * invTileSize; \ float2 uvOffsetX = float2(uvOffsetXY.x, 0); \ float2 uvOffsetY = float2(0, uvOffsetXY.y); \ \ float2 tileX = float2(tileQuad.x, 0); \ float2 tileY = float2(0, tileQuad.y); \ float2 tileXY = tileQuad; \ \ float4 texelX; \ float4 texelY; \ float4 texelXY; \ \ float2 texelXuv = float2(1.0 - texelPointUV.x, texelPointUV.y); \ float2 texelYuv = float2(texelPointUV.x, 1.0 - texelPointUV.y); \ \ // Fetch 2th texel in X corner \ if (tileQuad.x != 0) \ texelX = FetchTile_##T(tileX, texelXuv); \ else \ texelX = Fetch(Tile(T,4), texelPointUV + uvOffsetX); \ \ // Fetch 3th texel in Y corner \ if (tileQuad.y != 0) \ texelY = FetchTile_##T(tileY, texelYuv); \ else \ texelY = Fetch(Tile(T,4), texelPointUV + uvOffsetY); \ \ // Fetch 4th texel in XY corner \ if (tileQuadCase == 1) \ texelXY = FetchTile_##T(tileX, texelXuv + uvOffsetY); \ else if (tileQuadCase == 2) \ texelXY = FetchTile_##T(tileY, texelYuv + uvOffsetX); \ else \ texelXY = FetchTile_##T(tileQuad, float2(1.0 - texelPointUV.x, 1.0 - texelPointUV.y)); \ \ // now remove the sign in quadPos and use it as the weighting of the filter \ quadPos = abs(quadPos); \ \ // 4 texels bilinear filter \ return lerp(lerp(texel, texelX, quadPos.x), lerp(texelY, texelXY, quadPos.x), quadPos.y); \ }*/ // Evaluate the tiles and quad of samples required for this fetch // if only need the main tile, then we are done, just return the fetched value // else, need to gather texels from different tiles and filter // First the texel at the point uv in main tile // we ll need the position of the fetch in the quad of the 4 texels to do proper filtering // Quad pos is signed properly in the uv space here so we can compute the uv offset for 3 other samples // Fetch 2th texel in X corner // Fetch 3th texel in Y corner // Fetch 4th texel in XY corner // now remove the sign in quadPos and use it as the weighting of the filter // 4 texels bilinear filter #define Declare_FetchTiled(T) \ float4 FetchTiled_##T(float2 uv) \ { \ float2 tileSize; \ Tile(T,4).GetDimensions(tileSize.x, tileSize.y); \ float2 invTileSize = 1.0 / tileSize; \ float2 tileQuad = 0; \ float tileQuadCase = EvalTileQuadAt(uv, invTileSize, tileQuad); \ \ float2 texelPos = uv * tileSize; \ float2 texelPointPos = floor(texelPos); \ float2 texelPointUV = (texelPointPos + 0.5) * invTileSize; \ \ if (tileQuadCase == 0) \ return FetchLinear(Tile(T,4), uv); \ \ float4 texel = Fetch(Tile(T,4), texelPointUV); \ \ float2 quadPos = (texelPos - texelPointPos) - 0.5; \ \ float2 uvOffsetXY = sign(quadPos) * invTileSize; \ float2 uvOffsetX = float2(uvOffsetXY.x, 0); \ float2 uvOffsetY = float2(0, uvOffsetXY.y); \ \ float2 tileX = float2(tileQuad.x, 0); \ float2 tileY = float2(0, tileQuad.y); \ float2 tileXY = tileQuad; \ \ float4 texelX; \ float4 texelY; \ float4 texelXY; \ \ float2 texelXuv = float2(1.0 - texelPointUV.x, texelPointUV.y); \ float2 texelYuv = float2(texelPointUV.x, 1.0 - texelPointUV.y); \ \ if (tileQuad.x != 0) \ texelX = FetchTile_##T(tileX, texelXuv); \ else \ texelX = Fetch(Tile(T,4), texelPointUV + uvOffsetX); \ \ if (tileQuad.y != 0) \ texelY = FetchTile_##T(tileY, texelYuv); \ else \ texelY = Fetch(Tile(T,4), texelPointUV + uvOffsetY); \ \ if (tileQuadCase == 1) \ texelXY = FetchTile_##T(tileX, texelXuv + uvOffsetY); \ else if (tileQuadCase == 2) \ texelXY = FetchTile_##T(tileY, texelYuv + uvOffsetX); \ else \ texelXY = FetchTile_##T(tileQuad, float2(1.0 - texelPointUV.x, 1.0 - texelPointUV.y)); \ \ quadPos = abs(quadPos); \ \ return lerp(lerp(texel, texelX, quadPos.x), lerp(texelY, texelXY, quadPos.x), quadPos.y); \ } // EvalTileQuadAt() // Evaluate the tile quad fetching situation at the specified 'texcoord' uv in the range [0,1] // in a tile containg 'size' texels, the function uses the inverse of size 'invTileSize' // the function compute the tileQuad which indicate the neighbor tiles touched by the fetching quad along X and Y. // // The function returns a CODE indicating the case: // 0 <=> tileQuad == ( 0, 0) : the quad is fully in the main tile, no need for other tiles // 1 <=> tileQuad == (+/-1, 0) : the quad is crossing a West or East border into another tile (but no North/South) // 2 <=> tileQuad == ( 0, +/-1) : the quad is crossing a North or South border into another tile (but not West/East) // 3 <=> tileQuad == (+/-1, +/-1) : the quad is crossing 2 borders in a corner, 4 tiles touched // float EvalTileQuadAt(float2 texcoord, float2 invTileSize, in out float2 tileQuad) { float tileQuadCase = 1; // initiate with 1 if the x axis catch left or right, cancel if x is in the main tile tileQuad = 0; float2 halfTexelUV = 0.5 * invTileSize; // detect the X axis cases if (texcoord.x < halfTexelUV.x) tileQuad.x = -1; else if (texcoord.x > (1.0 - halfTexelUV.x)) tileQuad.x = +1; else tileQuadCase = 0; // cancel the first neighbor tile if x is in the main tile // detect the Y axis cases if (texcoord.y < halfTexelUV.y) { tileQuad.y = -1; tileQuadCase += 2; } else if (texcoord.y > (1.0 - halfTexelUV.y)) { tileQuad.y = +1; tileQuadCase += 2; } return tileQuadCase; } #define Declare_Tiles_And_FetchTiled(T) \ Declare_TiledArray(T) \ Declare_FetchTile(T) \ Declare_FetchTiled(T) #endif