So, I've got my implementation of virtual textures finally fully complete - and at that moment I noticed some very annoying issue - when I'm mapping virtual -> physical coordinates, then I can clearly see that vertices on the edge are mapping the first row or column of next tile. Note. I'm using virtual texture as a height map - therefore reading it per-vertex.
Suppose I have 64 x 64 tile size, and my physical texture has 8 x 8 tiles (for simplicity - and this also matches the test scenario I'm using). I'm mapping virtual texture on the plane (like height map), and edges of the tile are clearly showing the adjacent tiles on 2 edges (and this happens only at level 3 and higher). So - this gave me today a major headache which I've been trying to solve, there is no way I was able to solve this properly.
I had an idea (most likely correct one) - if I'm not able to map tile properly, i.e. edge vertices are reading value outside of the tile - then I could simply use 62 x 62 tile, and having 1 pixel border. On paper it sounds nice, but I got a bit too messed in the math. In vertex shader the mapping of virtual -> physical coordinates look like this:
float2 physicalTexCoord = pageIndirectionTableData[page].xy + virtualTexCoord * pageIndirectionData[page].zw;
Where:
- virtualTexCoord - represent virtual texture coordinates
- pageIndirectionTableData - contain offset (in x and y channels) and scale (in z and w channels)
- page - which physical page id is current processed vertex located in (the read from virtual -> physical is done 1 line above)
Now, this seems correct to me, and apart from that annoying edge of each tile being nuts - I have everything selected properly. To get rid of that, I have to add border, which should change how pageIndirectionTableData is calculated. Which now is:
int virtualx = address % (int)mPages[0];
int virtualy = address / (int)mPages[0];
float scalex = 1.0f / (float)mPages[0];
float scaley = 1.0f / (float)mPages[1];
float offsetx = (float)virtualx * scalex - baseoffsetx * scalex / basescalex;
float offsety = (float)virtualy * scaley - baseoffsety * scaley / basescaley;
mPageDescData[page * 4 + 0] = offsetx;
mPageDescData[page * 4 + 1] = offsety;
mPageDescData[page * 4 + 2] = scalex / basescalex;
mPageDescData[page * 4 + 3] = scaley / basescaley;
Where:
- virtualx and virtualy - are X and Y coordinates of page in physical texture (i.e. 2, 0 represent page that is located between (128, 0) and (192, 64) with 64 x 64 page size).
- mPages - is representing how many physical pages are there along X and along Y direction in texture (for current test mPages[0] = mPages[1] = 8)
- baseoffsetx and baseoffsety - represent location in virtual texture (0.25, 0.25) means that they point to the location of (64, 64) in my test scenario with 64 x 64 tiles and 4 x 4 tile size)
- basescalex and basescaley - represent scaling, i.e. miplevel of the tile - 1.0 means miplevel 0 (I.e. top level where tile contains whole texture), value of 0.25 means miplevel 2
Adding border (with given pixel size - which is 1.0 / physicalResolution) means that I need to scale a bit differently and add offset after (which needs to be recalculated too), but I got a bit confused with the math (as I'm mostly using 0.0 - 1.0 when working with texture coordinates.
If anyone sees some issue (or experienced similar), any help is appreciated.
Thank you for reading!