123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- #ifndef NINE_SLICING
- #define NINE_SLICING
- // Returns one of two float values depending on the value of the provided condition variable. This
- // is provided as an alternative to if/else branching, which is inefficient on GPUs.
- // condition: The condition to evaluate.
- // ifTrue: Returned if cond is true.
- // ifFalse: Returned if cond is false.
- float _ifElsef(bool condition, float ifTrue, float ifFalse) {
- return (ifTrue * (condition > 0)) + (ifFalse * (condition == 0));
- }
- // A struct containing the texture coordinate information of a nine-sliced fragment.
- struct nineSliceResult {
- // A UV coordinates for a texture.
- float2 uv;
- // A partial derivative of UV coordinates respective to the screen-space x-coordinate.
- // The derivative represents the rate of changes between texture coordinates in two adjacent
- // fragments, and is used for passing into the `tex2D` function for selecting the the correct
- // mipmap level of the texture.
- float2 ddx;
- // A partial derivative of UV coordinates respective to the screen-space y-coordinate.
- // The derivative represents the rate of changes between texture coordinates in two adjacent
- // fragments, and is used for passing into the `tex2D` function for selecting the the correct
- // mipmap level of the texture.
- float2 ddy;
- };
- // Nine-slices the UV coordinates of a fragment within the specified bounds. Nine-slicing is used to
- // tile the applied texture in the following manner:
- //
- // + +
- // (0, 1) | | (1, 1)
- // +----------------------+
- // | | tiled | |
- // | | x | | upper
- // +----------------------------+ bound
- // | t | | t |
- // | i | tiled | i |
- // | l y| x & y | l y|
- // | e | | e |
- // | d | | d |
- // +----------------------------+ lower
- // | | tiled | | bound
- // | | x | |
- // +----------------------+
- // (0, 0) | | (1, 0)
- // + +
- // left right
- // bound bound
- //
- // It offers functionality similar to Unity's inbuilt nine-slicing for sprites:
- // https://docs.unity3d.com/Manual/9SliceSprites.html.
- // pos: The (x, y) coordinates of the fragment being textured, relative to the origin of the face.
- // faceSize: The (width, height) of the face being nine-sliced.
- // texSize: The (width, height) of the base texture.
- // leftBound: The position of the left nine-slicing boundary normalized to the width of the texture
- // (in the range 0 to 1 inclusive). Tiling on the U axis only occurs to the right of leftBound.
- // rightBound: The position of the right nine-slicing boundary normalized to the width of the
- // texture (in the range 0 to 1 inclusive). Tiling on the U axis only occurs to the left of
- // rightBound.
- // lowerBound: The position of the lower nine-slicing boundary normalized to the height of the
- // texture (in the range 0 to 1 inclusive). Tiling on the V axis only occurs above lowerBound.
- // upperBound: The position of the upper nine-slicing boundary normalized to the height of the
- // texture (in the range 0 to 1 inclusive). Tiling on the V axis only occurs below upperBound.
- nineSliceResult nineSlice(
- float2 pos, float2 faceSize, float2 texSize,
- float leftBound, float rightBound, float lowerBound, float upperBound) {
- // Normalize relative to texture size.
- float2 normalFaceSize = faceSize / texSize;
- float2 normalPos = pos / texSize;
- // Calculate the size of the center fragment of the face.
- float centerFaceSizeX = normalFaceSize.x - leftBound - (1 - rightBound);
- float centerFaceSizeY = normalFaceSize.y - lowerBound - (1 - upperBound);
- // Calculate the size of the center fragment of each tiling;
- float centerTexSizeX = rightBound - leftBound;
- float centerTexSizeY = upperBound - lowerBound;
- // Calculate the number of tilings in the center fragment of the face.
- int tileNumX = max(round(centerFaceSizeX / centerTexSizeX), 1);
- int tileNumY = max(round(centerFaceSizeY / centerTexSizeY), 1);
- // Calculate the continuous UV used for calculating the derivative of the UV.
- float2 continuousUV = float2(
- pos.x / faceSize.x * (centerTexSizeX * tileNumX + leftBound + (1 - rightBound)),
- pos.y / faceSize.y * (centerTexSizeY * tileNumY + lowerBound + (1 - upperBound)));
- // Calculate bounding conditionals.
- bool isLeftBound = normalPos.x < leftBound;
- bool isRightBound = normalPos.x > normalFaceSize.x - (1 - rightBound);
- bool isLowerBound = normalPos.y < lowerBound;
- bool isUpperBound = normalPos.y > normalFaceSize.y - (1 - upperBound);
- // Calculate UV coordinates within each bound.
- float leftBoundU = normalPos.x;
- float rightBoundU = 1 - (normalFaceSize.x - normalPos.x);
- float centerBoundU = lerp(leftBound, rightBound,
- (normalPos.x - leftBound) * tileNumX / centerFaceSizeX % 1);
- float lowerBoundV = normalPos.y;
- float upperBoundV = 1 - (normalFaceSize.y - normalPos.y);
- float centerBoundV = lerp(lowerBound, upperBound,
- (normalPos.y - lowerBound) * tileNumY / centerFaceSizeY % 1);
- // Construct final UV coordinates based on the fragment's location in the face.
- float u = _ifElsef(isLeftBound,
- leftBoundU,
- _ifElsef(isRightBound,
- rightBoundU,
- centerBoundU));
- float v = _ifElsef(isLowerBound,
- lowerBoundV,
- _ifElsef(isUpperBound,
- upperBoundV,
- centerBoundV));
- // Construct the nine-slicing result struct.
- nineSliceResult result;
- result.uv = float2(u, v);
- result.ddx = ddx(continuousUV);
- result.ddy = ddy(continuousUV);
- return result;
- }
- #endif
|