Does anyone know how to get a 16bit texture to displace vertices in the vertex shader? Is it something I can set in the shader from GDScript?

I thought I could maybe store one byte in the red channel and the other in the green and bitshift it back inside the vertex shader, but texelFetch returns a vec4 of floats between 0.0 and 1.0 which makes it a little hard...

Here I'm using the red channel of a PNG (all channels are the exact same result) and you can see the steps in the height.

@"Lethal Raptor Games" said: Do they remain accessible as 16 bits in the shader though?

No, you need to load a texture in the Radiance HDR or OpenEXR format. GIMP allows saving images in OpenEXR (.exr extension), so you can use that to convert your PNG to an OpenEXR image.

@Calinou Hmm, always wondered if there was any particular reason behind why 16 bit png's aren't importable though? Or just something no body has implemented yet?

@Calinou said:

@"Lethal Raptor Games" said: Do they remain accessible as 16 bits in the shader though?

No, you need to load a texture in the Radiance HDR or OpenEXR format. GIMP allows saving images in OpenEXR (.exr extension), so you can use that to convert your PNG to an OpenEXR image.

How should an OpenEXR image be used within a shader? I tried to use it the same as a PNG but I saw no change to the mesh. I use it like this;

uniform sampler2D mymap;

vec4 data = texelFetch(mymap, ivec2(x, z), 0); VERTEX.y = float(data.r) * 1000.0;

@Megalomaniak said: @Calinou Hmm, always wondered if there was any particular reason behind why 16 bit png's aren't importable though? Or just something no body has implemented yet?

It's a design decision to avoid wasting memory when you don't need higher precision than 8 bpc. Most people are not even aware that PNGs can store more than 8 bpc, yet most image editing software will happily create such PNGs and not warn people about the VRAM/storage size implications.

By forcing the use of .hdr/.exr for high dynamic range images, there is no such ambiguity.

How should an OpenEXR image be used within a shader? I tried to use it the same as a PNG but I saw no change to the mesh. I use it like this;

It should work out of the box. Double-check that the OpenEXR image actually contains higher precision than 8 bpc. You can use the ImageMagick identify command to do so.

I verified it is 16 bit. However the only way I could get it too work was to load a r16 file and put each byte into an image and assign to a usampler2D slot in GDScript.

I could kind of get it working by dragging the EXR image onto a samlper2D slot in the material, but the data wasn't quite complete. It looked like it was still only reading from one channel.

Anyway, this is the code I used to get it working (from the link I posted earlier)

func _createHeightMapTexture(sourceMap: TerrainMap):
	var image = Image.new()
	var data = PoolByteArray()
	data.resize(1024 * 1024 * 2)
	
	var mapX = 0
	var mapZ = 0
	
	var index = 0
	for z in 1024:
		for x in 1024:
			data[index] = sourceMap.getByte(mapX + x, mapZ + z, 0)
			data[index + 1] = sourceMap.getByte(mapX + x, mapZ + z, 1)
			index += 2
	
	image.create_from_data(1024, 1024, false, Image.FORMAT_RH, data)
	#image.save_exr("res://Terrain//Data//Map.exr")
	
	var texture = ImageTexture.new()
	texture.create_from_image(image, 0)
	material.set_shader_param("heightmap", texture)

And in the vertex shader;

uniform usampler2D heightmap;
uvec4 godot_hack_mix(uvec4 a, uvec4 b, bvec4 c) {
	return uvec4(c.x ? b.x : a.x, c.y ? b.y : a.y, c.z ? b.z : a.z, c.w ? b.w : a.w);
}
uvec4 getExponent(uvec4 data) {
	return godot_hack_mix(uvec4((uvec4((ivec4((data & uvec4(0x7F800000)) >> uvec4(23)) - ivec4(127)) + ivec4(15)) & uvec4(0x1F)) << uvec4(10)),
	uvec4(ivec4(31 << 10)),
	equal((data) & uvec4(0x7F800000), uvec4(0x7F800000)));
}
uvec4 getValue(uvec4 data) {
	uvec4 exponent = getExponent(data);
	return godot_hack_mix(((data & uvec4(0x007FFFFF)) >> uvec4(13)) | exponent | (((data >> uvec4(31)) & uvec4(1)) << uvec4(15)),
	(((floatBitsToUint(uintBitsToFloat(data & uvec4(0x7FFFFFFF)) + 1.0/16384.0) & uvec4(0x007FFFFF)) >> uvec4(13))) | (((data >> uvec4(31)) & uvec4(1)) << uvec4(15)),
	lessThanEqual((data) & uvec4(0x7F800000), uvec4(0x38000000)));
}
float getMapValue(int x, int z) {
	uvec4 data = texelFetch(heightmap, ivec2(x, z),0);
	data = getValue(data);
	return float(data.r) / heightScale;
}
a year later