ImageRaster problems with RGB16F Images

Hi, I am using procedural generation to generate my heightmaps for TerrainQuads. I have a working rolling world generator working with procedural generated 8-bit heightmaps , which works perfectly.

However i needed taller mountains and deeper oceans. So i am converting my system to use 16 bit heightmaps to get the fidelity i need.

I thought it would be as simple as changing

   Image im2 = new Image(Image.Format.RGB8, isize, isize, BufferUtils.createByteBuffer((isize) * (isize)* 3), null);

to

   Image im2 = new Image(Image.Format.RGB16F, isize, isize, BufferUtils.createByteBuffer((isize) * (isize)* 3), null);

(The doco for ImageRaster says to stick toRBG16F rather than RGB16 , not sure why)
However when attempting to setPixel on the ImageRaster, an exception gets thrown:

SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.IllegalArgumentException
	at java.nio.Buffer.position(Buffer.java:236)
	at com.jme3.texture.image.ByteAlignedImageCodec.writePixelRaw(ByteAlignedImageCodec.java:67)
	at com.jme3.texture.image.ByteAlignedImageCodec.writeComponents(ByteAlignedImageCodec.java:125)
	at com.jme3.texture.image.DefaultImageRaster.setPixel(DefaultImageRaster.java:111)
	at Noise.RollingNoise.writeFaToImageRaster(RollingNoise.java:126)
	at Noise.RollingNoise.FaToImage(RollingNoise.java:79)

etc

Obviously setPixel is expecting an 8bit canvas to draw on … couldn’t find anything in the doco which was immediately obvious to get this to work on 16bit images.

Relevant functions below. Heightmap is generated as an array float[513][513] before being converted from Float array → ImageRaster → Image → AbstractHeightmap

If someone can point me to any bit of code that generates a 16bit heightmap programatically rather than loading it from a RAW file that would probably be enough for me to crack the problem by myself.

Thanks
Arindrel

    public Image FaToImage(float[][] faIn){
        
        //isize is either 512 or 513 depending if we need fencepost (512 for minimap images, 513 for actual heightmaps)
        int isize= faIn.length;
        Texture tex = m.getAssetManager().loadTexture("Interface/"+(isize)+".png");
        
        //Working code for 8 bit, trying to swicth to 16bit
        //Image im2 = new Image(Image.Format.RGB8, isize, isize, BufferUtils.createByteBuffer((isize) * (isize)* 3), null);
        Image im2 = new Image(Image.Format.RGB16F, isize, isize, BufferUtils.createByteBuffer((isize) * (isize)* 3), null);
        
        tex.setImage(im2);
        ImageRaster raster1 = ImageRaster.create(tex.getImage()); 
        
        writeFaToImageRaster(faIn,raster1);
        
        Image img1= tex.getImage();
        
        return img1;
    }


public void writeFaToImageRaster(float[][] data, ImageRaster r1){
        //isize is either 512 or 513 depending on fencepost
        int isize= data.length;
        int xloop = isize;
        int yloop = isize;

        for (int y = 0; y < yloop; y++) {
            for (int x = 0; x < xloop; x++) {
                
                if ((y<isize)&&(x<isize)){

                    //clamp - at the last possible moment clamp
                    float value = data[x][y];
                    if (value>1.0f){value=1.0f;}
                    if (value<0.0f){value=0.0f;}
                    r1.setPixel(y, x, new ColorRGBA(value, value, value, 1));
                
                }
                else{
                    r1.setPixel(x, y, new ColorRGBA(1f,1f,1f, 1));
                }
                
                
            }
        }
    }

The solution is to have your grid of terrain also page vertically as opposed to just horizontally. That is to say that you layer chunks of terrain on top of each other. Each cell can now have 8bit fidelity as opposed to one single cell, so you are not limited by height anymore.

The solution is to have your grid of terrain also page vertically as opposed to just horizontally. That is to say that you layer chunks of terrain on top of each other. Each cell can now have 8bit fidelity as opposed to one single cell, so you are not limited by height anymore.

Yeah I considered this and played around with the idea in code already, with each chunk having a height offset. It gets unmanageable when you have steep features (Cliffs, ravines, canyons etc) which do not neatly follow the borders of the chunk grid. Because the terrainGrids have to be square you are limited to what height delta you can have within that square area.

Although what you propose is neat, and thanks for the suggestion, however it dosnt really work for my use-case.

Is it impossible to create/edit 16bit height-maps in-code?

Not at all - in the worst case, you just use a couple channels in an 8-bit rg or rgb image and combine those into a single 16-bit channel in the shader. I highly doubt you’ll have to do that, though. OpenGL has support for pretty much any crazy pixel format you can dream up.

So the code you posted works fine exactly as is with the other image format? Or were there other differences?

So the code you posted works fine exactly as is with the other image format? Or were there other differences?

The code as posted works fine with RGB8 images. It crashes with an exception with RGB16F images at the point where setPixel is called.

Obviously RGB8 heightmaps show either “stepping” or limited height, depending on how you setLocalScale() on the final TerrainQuad . It is these artifacts that are my reason for investigating 16bit heightmaps.

Since posting this I have been poking rounf the code in com/jme3/terrain/heightmap/RawHeightMap.java

I noticed a constructor method that accepts a float array - thought this could be a potential solution, im procedurally generating a float heightmap in code anyway, would be neat just to unwind the array and pass it… but noticed this coerces the data back into 8BIT - is this intentional?

public  RawHeightMap(float heightData[]) {
        this.heightData = heightData;
        this.size = (int) FastMath.sqrt(heightData.length);
        this.format = FORMAT_8BIT;
    }