Game Development Reference
uint faceID, // From SV_PrimitiveID
float2 faceUV, // Position within the face
uint nLog2, // Log2 of the resolution we want
int texWidth, // Atlas texture width
int resOffset, // Prefix sum for this resolution
int rowOffset, // Start of resolution block in atlas
int borderSize ) // Texel thickness of border on each face
// Here we assume a square aspect ratio.
// A non-square aspect would simply scale the height
// relative to width accordingly.
float faceWidth = 1 << nLog2;
float faceHeight = faceWidth;
float borderedFaceWidth = faceWidth + 2*borderSize;
float borderedFaceHeight = borderedFaceWidth;
int nFacesEachRow = ( int )texWidth / ( int )borderedFaceWidth;
int iFaceWithinBlock = faceID - resOffset;
float2 faceOrigin = float2 (
(iFaceWithinBlock % nFacesEachRow) * borderedFaceWidth,
(iFaceWithinBlock / nFacesEachRow) * borderedFaceHeight
+ rowOffset );
// Take face UV into account.
// Still in texel units, but generally not
// an integer value for bilinear filtering purposes.
float2 uv = float2 (faceWidth, faceHeight) * faceUV;
uv += float2 (nBorderSize, nBorderSize);
uv += faceOrigin;
// Finally scale by texture width and height to get
// value in [0,1].
return float2 (uv) / float2 (texWidth, texHeight);
Listing 2.2. Go from face UV to atlas UV.
Scale and offsets are applied to get the face UV range of [0,1] mapped into
the atlas UV, including an offset to get to the right resolution block and another
to put the face texture origin (0,0) inside the face border. Listing 2.2 details the
process of computing a UV within the packed Ptex atlas.
The last steps are to do the bilinear filtered lookup for each LOD we need,
and the final trilinear lerp between them.
There are discrepancies in resolution that translate to discontinuities when ap-
proaching a polygon edge from either side. This is illustrated in Figure 2.3. This
can happen when the gradient used for mip selection changes as the edge of a
polygon is crossed.
However, this is not particular to packed Ptex and is fur-