Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.59k stars 244 forks source link

Smooth terrain and custom stream #123

Open Anders333 opened 4 years ago

Anders333 commented 4 years ago

I need create large terraind so i decided to create own voxel stream. But looks like out_buffer.set_voxel() does not care about first parameter as float. So when i put 0.5 or 1.0 as first parameter, height of voxel always the same. We can see that on screenshot below: frpg I didnt find way to set iso(sdf) parameter. Can i do smooth terrain with my own voxel stream logic?

TokisanGames commented 4 years ago

Try set_voxel_f

https://github.com/Zylann/godot_voxel/blob/master/doc/api/VoxelBuffer.md#-void-set_voxel_f--float-value-int-x-int-y-int-z-int-channel0-

Anders333 commented 4 years ago

Try set_voxel_f

https://github.com/Zylann/godot_voxel/blob/master/doc/api/VoxelBuffer.md#-void-set_voxel_f--float-value-int-x-int-y-int-z-int-channel0-

Thx! Now i see some vertical little offset. Annotation 2020-02-15 005253 Looks like need a lot of work on correct voxel position for smooth terrain, coz default height from image is not enough.

Zylann commented 4 years ago

To obtain smooth terrain you need a distance field value to put in set_voxel_f, which should vary slow enough to be sampled properly, which is why it is often scaled to adjust it (if it varies too fast the value will clip to -1 or 1 and rarely in between, causing blocky result).

Is there still something you need help on about that subject or is the issue about set_voxel solved?

Anders333 commented 4 years ago

To obtain smooth terrain you need a distance field value to put in set_voxel_f, which should vary slow enough to be sampled properly, which is why it is often scaled to adjust it (if it varies too fast the value will clip to -1 or 1 and rarely in between, causing blocky result).

Is there still something you need help on about that subject or is the issue about set_voxel solved? Thx, as i understand, set float to zero makes level of voxes as we see in data? In case set just int at height 1 we receive height on 0.5.But when i set float 0.0 on the top of previos voxel, i have result height of terrain on 1.0. But I have a little problem. I set float value to voxel, and they show me unexpected result: thisismadness So what i see, -1.0 give me expected result : +0.5 in height , but 0.5 looks like +0.34(not +0.25), and 0.25 like +0.16 (not +0.125)

Zylann commented 4 years ago

Ok, this ended up as a long one but here is the explanation:


The SDF value to store in the grid is not an actual distance directly corresponding to what you see in the world. It is a normalized value between -1 and 1. However, by ratio, it should correspond to the degree of "filling" of a cell.

Also, smooth voxels use interpolation to be smooth. Because of this, if you set a single voxel of matter in the middle of the air at (x,y,z), it will appear as a diamond centered on that position, rather than the center of a cube that would go from (x,y,z) to (x+1, y+1, z+1).

image So a value of -1.0 means the voxel will indeed be of size 1, but centered. So 0.5 on each side, and here as a diamond because all neighbors are air (1.0).

Here with different SDF values: image

Here a patch of 2x3 voxels with value 1.0: image

And value 0.5: image

Zylann commented 4 years ago

Now if I measure it using a plane, at a height where the voxel would be if its growth was linear: image

There is indeed a small divergence, which seems to follow a curve like this: image

It can also be reproduced with the old smooth mesher (which was basically marching cubes from Ogre's dual marching cubes). Both were written from scratch and don't have any code in common.

Here is how Transvoxel interpolates two samples to determine a vertex position:

// p0 and p1 are integer positions of the two corners of the cube we are in
// sample0 and sample1 are -128..127 integer representations of the SDF at the corners
int ti0 = (sample1 << 8) / (sample1 - sample0);
int ti1 = 0x100 - ti0;
Vector3i primary = p0 * ti0 + p1 * ti1;
Vector3 primaryf = primary.to_vec3() * FIXED_FACTOR;

https://github.com/Zylann/godot_voxel/blob/488ea32ee1982866d337b76e17a9840c2d45d713/meshers/transvoxel/voxel_mesher_transvoxel.cpp#L432

There you go, here is the curve ti1 follows, which controls how much of p1 the vertex will have: image

Now, a practical explanation of this, is a direct consequence of what an isosurface actually is: The visible surface is the place where interpolated SDF values cross zero. If you chart this in 2D, that's how it looks like:

image

Now, take voxel 0 value, and increase it linearly... and you will notice the point where the line crosses 0 does not move linearly. In fact, it will only move linearly if you increase both voxels up.

But, since voxels are sampled in normalized values, it's not possible to store values higher than 1, or lower than -1. In fact, having such values so close to the surface means they are clipping, and that can cause blockyness. This problem can be attenuated by multiplying your SDF by a small factor like 0.1, to exploit the sampling precision better, keeping the curve within -1..1. It will provide more headroom, while not changing the intersection point. This is what improved heightmap generators a lot recently. They used to clip too early due to generating actual distances into voxel values, now they generate 1/10e of that https://github.com/Zylann/godot_voxel/issues/73#issuecomment-578526202 In general, don't try to set specific values with set_voxel_f. Always use something akin to a distance, unless you aim for a specific look.

I ended up listing far more things than I intented, but I hope this helps.

Anders333 commented 4 years ago

Thanks for wide answer, now i see it is not simple. I planned to make editable terrain that can support flat regions with angles< 45 degrees. Looks like need to calculate some constant multiplier for that. But after terrain will generated, i need store data in another stream, like voxel_stream_file, coz i need to store caves and another modification of voxel terrain. Thx for that awesome tool!