Question/Discussion: Best practice to import Terrain generated in Blender (and similar)
MJacred opened this issue · 9 comments
For anyone stumbling over this issue: I wrote a Blender addon bmesh-to-raw to correctly export a mesh to raw file
The Problem
Currently, this plugin allows (out-of-the-box) to create terrains by
a) importing a file with height info
b) using a dialog with some adjustable parameters
c) sculpting by hand using OpenEXR brushes
d) procedurally by code (I think)
b) is not that useful if you desire a bigger map with more variation and c) takes just really long.
For a) there are a lot of terrain generators like world machine, world creator, gaia, gaea, etc. But from what I could see, there is a lack of control over the finer details regarding size and shape (or maybe they just didn't showcase that).
Either way, it's also possible to create heightmap terrains in general purpose 3D modeling software like Blender which allow a great deal of freedom on how to create and edit them quickly. Though there is the issue of getting it into Godot properly.
From time to time there are issues popping up regarding transferring a terrain; currently open are
The Goal
This plugin can import file formats such as PNG, OpenEXR (16-bit (half-precision float mode) greyscale), XYZ and Radiance HDR - and well… RAW (unsigned integer 16-bit), where all except PNG expect real height information and not floating point values between 0-1.
Exporting from Blender to PNG is doable using a node shader (#90 (comment)), but Godot can only import PNG as 8-bit, which creates stepping artifacts.
Blender also supports these formats afaik, though, I could only get OpenEXR
to work even remotely. And it still has issues.
Export to OpenEXR in Blender
so I tried what @Zylann mentioned
Did you actually try to put the camera under the terrain in Blender, make it orthographic covering the exact size of the terrain, output height in displacement instead of surface and somehow export the resulting depth as OpenEXR from the compositor instead?
I don't know about how to use the result if you put the height info in displacement port, but I tried sth. similar: still output into surface, but in compositor, read the z-depth of the camera…
But, I still have trouble getting a landscape from Blender to Godot:
blender test file:
terrain-baker.blend.zip
I tried exporting an OpenEXR file using the compositor in blender (select Viewer node, go to Item
in the Node tab and click Save This Image
as .exr file).
Current issues with export to OpenEXR
Imported into Godot (Godot 3.3.4; addon is version 1.5.2), I get the following issues:
I can get rid of the ripped corners, if I place the camera above instead and compensate for the camera position by modifying the z-depth values, but then the corners stretch really high (which can be fixed using "flatten" in the addon)
Overall, using either of the 2 approaches, I don't have the stair-cased issue, but the results are never smooth:
The compositor relies on the render to get the depth buffer, but I don't think color management is important here. Though I tested all kinds of combinations (and the sequencer was always set to linear
)
- sRGB + Standard
- sRGB + Raw
- None
I also tried what @RonanZe mentioned in his comment here by importing the OpenEXR file into krita and saving as R32, but then on import in Godot, it said that the heightmap is not square (maybe this is because of the ripped/missing corners? I didn't test the OpenEXR file where the corners where upwards stretched once imported into Godot).
Any help would be greatly appreciated!
Maybe @RonanZe can elaborate more on the workflow they succeeded with!?
At the moment I have no more info on how best to proceed. I was always thinking that if you can get an image out of Blender with sufficient depth precision and not altered by color management, importing into Godot should be fine.
If you get artifacts in the shading, it could be related to the actual precision Blender gives, which might be lower that what the plugin usually works with for some reason? 16-bit float is just enough but what goes inside can still have lower-precision (note that if you had to translate the terrain a lot vertically to center it on Y=0 after import, that might also explain precision loss, as higher numbers loose precision in this format).
So far it seems Blender isn't thought for exporting proper heightmaps? Or maybe there is an even more specific setup to use?
Hm… it's possible there is a precision loss in the Blender shader or compositor…
(I did no translation or any adjustments in Godot)
There is one more approach that should be possible in vanilla Blender: first render to a 16-bit greyscale image file (without color mgmt) and then import the file in Blender and remap using the compositor as seen here: https://www.youtube.com/watch?v=UPwoG_8KGa4
I'll try it this weekend.
EDIT: If the above does not work out, I'll try this approach first to create a .raw file: https://github.com/Mezaka/UnityHeightmapBaker
If that also does not work, I'll try writing a Blender addon which directly reads the vertex positions Vector3.UP and creates a RAW (unsigned integer 16-bit) file. (While looking at the terrain in Blender from above) I take I would have to read the vertex positions from left to right, bottom to top?
Any tips on sorting that mess or anything else?
if my math skills did not fail me, then I guess I need this formula
h_range = max_up_value - min_up_value
my_unsigned_16_bit_int = (blender_up_value * 65535.0) / h_range
Hm… So, I tried the approach to png to exr
and it did not remove the "grid".
I did some other things
- update addon to 1.6.1
- in Blender, I added a subdivision modifier (render level: 2)
After rendering with higher mesh resolution, the grid looks more like little bumps (increasing the render level more made no difference):
(remapping: 0..1 to 0..320 using the meshes dimensions)
Then I tried creating a heightmap in gimp (I think I got the color specs right):
And if I remap the values from 0..1 to bigger than 0..50 (roughly), then I get artifacts:
Either way, once I do smooth
operations, it smears (the bumpier it is, the worse it gets):
Playing with Raise
+ Smooth
does not help.
There is probably sth. in the smooth operation using brush which does not like those mini bumps.
Possible action in the addon: Maybe a Smooth whole terrain by strength x
logic would help.
(side note: The blender code for remapping in compositor uses normal c++ float…)
To conclude the remapping approach: The difference in the remapping has a much stronger limitation than anticipated
Well, don't know if this can help, but I solved my import problems converting png 16-bit to raw 16-bit shorts unsigned with one line of code with imagemagick:
magick stream -map r -storage-type short image.png image.raw
But PNG should have the height data only in range 0..1. So you probably scaled the y-axis in map_scale
?
Thanks, I'll try it this weekend.
Ok, I finally did some testing with magick
Note: found issue:
- rasing terrain is not possible anymore (plugin v 1.6.1)
- happened either after import of raw file or after i first set one textureset, deleted it and loaded different images into it
- going back into project overview and back into project fixed it
And using the command above definitely improves the results (thanks @Naurk ):
But the wireframe is uneven on slope tops -> cannot be fixed with hterrain brushes:
@Zylann: being able to set a float for min and max heights on import should help, because my terrain is not in the size of full integers. in _import_heightmap
it is used in a float-only context anyway. so there should be no harm. This could fix the unevenness
see blender vs godot (same mesh resolution -> see 1x1x1 cube on top):
Still Weirdness
with new commit 778907a, there's now more control, but there is still some weirdness there
hterrain (used a script in Blender to get highest and lowest vertex.z position for accuracy)
Proposal: recalculate Heightmap from lower LOD to fix imported map
one way to allow fixing issues with imported maps would be to recalculate higher LODs
- button:
autocorrect using lower LOD
- input field:
-1
,-2
(the source LOD, as an offset to "highest-detail lod")
LOD -1
is already smoother (better for spiky terrain)
LOD -2
would be even more smooth (better for rounder terrain)
Comparison: Godot <> Blender (using magick process)
i lined up the vertical lines: blender (orange) <> hterrain (blue)
@Naurk: As you got so good results using the magick
process, could you share an example setup? Would be highly appreciated
I also tried https://github.com/Mezaka/UnityHeightmapBaker, and had to
- change line
bytes = bytearray(b'\x00\x0F')
tobytes = bytearray(b'\x00')
orbytes = bytearray(b'\x10')
, else the raw file was non-square and could not be imported - import as big endian
But the result was quite chunky
His import looked smoother, but that might be due to his scaling?
As it was rendered at 1025x1025, the height is 0.0-512.5.
But dividing map size by 2 (to get this scaling on import) did not improve the spikes…
@Zylann: I wrote a Blender addon bmesh-to-raw to correctly export a mesh to raw (provided it's clean)
some screenshots: https://github.com/cubiest/bmesh-to-raw/tree/main/docs
As this issue is more or less stale and one sound solution (not counting imagemagick) has been found, I'll be closing this issue.