Jump to content
  • Advertisement
yonisi

DX11 Terrain collision issue (Data and rendering doesn't match)

This topic is 384 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

I'm working on a terrain for some project I'm involved in. The terrain is pretty large and represents a ~90 Meter (3 arc-second) resolution for a surface that is 1024x1024 KM in size. For that purpose the Heightmap texture is large and its size is 11072^2. Actually the original size was 11052^2, but I had to pad it to be 11072^2 as the rendering algorithm is using 64x64 patches for the DX11 64-limit tessellation to divide the terrain exactly as necessary, so 11072 / 64 gives exactly 173 patches, which is the number of patches entering the pipeline.

Now, I have 2 methods for rendering the terrain which yields the EXACT same results (There is a very minor textures difference but the vertices from both methods are exactly one on top the other):

1. Basic method that renders the heightmap as-is with the tessellation pipeline - I had to ditch that method due to severe FP accuracy issues with the art textures, but still the geometry is OK.

2. More complicated method by Nvidia as described here - https://www.yumpu.com/en/document/view/32144510/directx-11-terrain-tessellation-nvidia-developer-zone - I used the Nvidia source code and manipulated it for my engine and use a constant heightmap without noise, rather than the original procedural noise they generate in the original code.

So, these 2 methods render EXACTLY the same geometry which should represent the heightmap 1:1. I'm sure it should be 1:1 because In the 1st method I input to the pipeline 173 quad patches which are then divided by the tessellator from 1-64 times (I'm actually using power of 2 fashion for the tessellation, but it shouldn't matter as the issue I have is anyway relevant only to the highest detailed tessellation).

The heightmap data is coming from a RAW texture contains 11072x11072 components of 16-bit values (i.e the size of the texture is ~232MB). As the heightmap contains values in feet from real world SRTM data, I'm just scaling it after loading from the texture to be grid local float values (i.e I have a FEET_TO_GRID value which is used for the scaling). Since I'm loading RAW texture as-is I consider it should be 100% accurate as it's not compressed like DDS texture. Technically for the loading I read the texture data into an array of 16-bit short values (cause data could be negative as well), and then I feet the scaled values into a float array of the same size as the heightmap, i.e 11072x11072, then I'm creating a SRV with 32-bit float format (i.e DXGI_FORMAT_R32_FLOAT).

Now to the issue - The float array used to create the SRV is saved in system RAM so I can use it for simple collision detection and triangles normals calculations. The calculations for the collision and normals are correct as I'm using Barycentric interpolation (Example here https://classes.soe.ucsc.edu/cmps160/Fall10/resources/barycentricInterpolation.pdf). Let's leave normals alone for now, I only care about the height. But I didn't managed to put objects EXACTLY on the terrain surface, usually objects hover a bit or sink into a bit, but I must have them exactly on the terrain surface, and I think it should work because I'm holding in system RAM exactly the same height field data as the most detailed tessellated patch  rendering. So I can't understand why my objects can't stick to the terrain.

Things I thought about and tried, and also some stuff I noticed that may give more info:

1. I've noticed that the heightmap is being "followed", I.e I can see the object moving with the terrain surface changes, but there is some shift or scale that cause the height to not be in sync. I tried to look in the rendering for such shift/scale distortion but couldn't find any, and since I have the same terrain rendered with 2 different methods, small chance that both cause the same distortion in the rendering if there was some shift/scale issue (I'd expected them to differ).

2. Using a constant value for the height when loading the heightmap and the object stands perfectly on the terrain, so there is no FP accuracy issue with the scaling factor etc.

3. Using sine wave instead of the heightmap values showed same as #1 - The object is moving in a sine wave way but not in-sync with the rendered surface.

4. I thought maybe since the tessellator divides the tris in "different" fashion in a given patch (i.e sometimes it's ABC/BCD and sometimes ACD/ABD). So I tried to change the collision computation to this and that fashion, but anyway both give ~same results, so it can't be due to "different tri-dividing fashion).

5. My main concern - Could it be that because the heightmap texture UV size is irregular, then the shaders introduce some distortion compared to the data? Maybe DX does some up/down scaling to a more "friendly" number for the Texture size and so creating the distortion?

Some reference pictures:

Both terrain methods rendered one on top of the other, you can see the vertices are 1:1 exactly. Black is 1st method and Red is the Nvidia method:

5hMJ8To.png

 

Crate sunk in the terrain, if I make it move then you can see it hovers/sinks at some points but still following the terrain shape, although mostly the movement changes aren't exactly in sync with the surface (And the distortion I suspect):

Jcw7VbR.png

 

Please help because I'm out of ideas. I know that in some games some distortion is allowed and expected as the collision data doesn't hold the full-resolution rendering details, but here it's not the case, and also cannot be accepted as this crate in the picture represents a pretty large object in the terrain I'm working on (i.e it's ~9x9 meters building/structure as it's size is 0.1 and the grid units are ~90 meters in size).

I thought of trying to output the vertices world positions somehow directly from the Domain shader (I'm not yet sure how exactly, if possible at all), or alternatively render to texture once with full tessellation on all patches (So data should be highest detail) and save the texture data to some array and compare it with the data I'm using for the height calculations. If you have other ideas, please share.

Thanx! :)

Share this post


Link to post
Share on other sites
Advertisement

I'm not sure if it's relevant but I had a similar issue that came down to the UVs I was using to sample the heights from my texture. I needed linear interpolation enabled for the sampler for some LOD blending reasons, which meant at each LOD level I needed to make sure I was hitting the texel center accurately for the relevant mip. I'm not at the PC with the code and shader to remind me of the details, but I do recall it being a total pain. Your comments about the issue appearing offset even when using a simple sine wave sounded awfully familiar :).

Edit: Actually nevermind - it sounds like you're loading rather than sampling the data... I'm not sure in that case.

Edited by Tessellator

Share this post


Link to post
Share on other sites

I don't know how you're deriving the height at a given point on the CPU, but off the top of my head your heightmap size is wrong - it should be 64*173+1 = 11073^2. To use an example given by somebody else -  if you want to erect 4 fence panels you need 5 fence posts - i.e. 1 more post than panel. Likewise, if you want to render 64*173 (11072) quads you need 64*173+1 heightmap points. What you're doing is sampling between the actual height points, which probably accounts for the offset you're seeing.

Share this post


Link to post
Share on other sites

Thinking about it, it could also just be caused by floating point rounding errors - does the error virtually disappear near the origin?

Personally I would resize the heightmap to 16385*16385 and divided it into 16 4097*4097 (or smaller) chunks and cull/render each individually. Powers of two are much easier to work with, especially when it comes to LODs.

Share this post


Link to post
Share on other sites

Thanx for the answers! Ths issue was resolved by Using the Nvidia based terrain and increasing the tris count (It has that option because it's based on "quad rings" with different sized patches, and since the all algorithm is screen-space optimized, reducing the patches size doesn't have THAT bad effect on performance) in a way that the heightmap is over sampled, then performing quad bilinear interpolation (same as it's done in the Domain shader) gets the correct altitude.

Also by using the Nvidia based algorithm I'm not bound to the 64-sized patches, so the Heightmap could stay at its original size of 11052.

 

To my surprise, using the low tris count version (of any of the 2 methods) with the padded Heightmap which should have been OK, still showed altitude differences at some points that I cannot explain. It looks like with the over-sampled version, the quads were smoothed, but with 2 tris, sometimes quads were having some unnecessary "break" in the middle (Where the 2 tris are divided) which caused the miss match issue.

On 10/28/2017 at 10:42 PM, Mk_ said:

Thinking about it, it could also just be caused by floating point rounding errors - does the error virtually disappear near the origin?

Personally I would resize the heightmap to 16385*16385 and divided it into 16 4097*4097 (or smaller) chunks and cull/render each individually. Powers of two are much easier to work with, especially when it comes to LODs.

Seems like the FP rounding is OK actually, so I left it as-is. I know powers of 2 are better but here the real world size of the Heightmap came to that size, so I prefer using it than going up to 16384 and do data-paging and also double the Heightmap size in VRAM. Also there is no mipmapping for the Heightmap (It's always sampled with 0 mip level), so LOD/mipmapping don't play here.

On 10/28/2017 at 10:12 PM, Mk_ said:

I don't know how you're deriving the height at a given point on the CPU, but off the top of my head your heightmap size is wrong - it should be 64*173+1 = 11073^2. To use an example given by somebody else -  if you want to erect 4 fence panels you need 5 fence posts - i.e. 1 more post than panel. Likewise, if you want to render 64*173 (11072) quads you need 64*173+1 heightmap points. What you're doing is sampling between the actual height points, which probably accounts for the offset you're seeing.

Yes that is indeed correct, bounded to the 64-sized patches I had to make the Heightmap 11073x11073 because otherwise the was a miss alignment, but not by 1 cell diff, but by 64! as the algorithm actually is rounding down the number of patches, so 11073 was the only way to go (And the initial sync issue was due to the fact that I missed the number of patches by 1!).

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!