How to create a Texture3D from layers

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Xqua

Alright I’ve been at this for a bit and the doc didn’t help me :frowning:

Just for context, my goal is to make a RayMarching shader through a 3D volumetric dataset from microscopy recording. I’ve done it in Unity in the past and I want to continue this project. But I want to switch to Godot, out of principles, and because the Node system is just amazing.

So here I am trying to create a simple RayMarching shader through a cube, and I need to pass my 3D texture so that I can sample from the volume in the fragment shader.

So, as per the documentation, I need to use the inherited function set_layer_data which takes an Image instance, and an layer Int. Ok all good !

Well no … the layer never gets set and pixels stay at [0.0, 0.0, 0.0, 1.0]

Here is my code sample so that one of you wizard might see what I am doing wrong …

extends MeshInstance

var mat = get_surface_material(0)

func _ready():
	# Instanciate a Texture3D
	var tex3D = Texture3D.new()
	# Create the texture with a 64 cube with RGB8 format, we won't need Alpha
	tex3D.create(64, 64, 64, Image.FORMAT_RGB8)
	# Instanciate an image this will serve as our layer for now
	var img = Image.new()
	# Let's give it the right dimension 64x64 and the right format
	img.create(64, 64, false, Image.FORMAT_RGB8)
	# Gotta Lock it to set a pixel (this is confusing ... Like, Lock? Why not "Unlock" it ??? Anyhow)
	img.lock()
	# Define out color
	var c1 = Color("#ffb2d90a")
	# And set the pixel
	img.set_pixel(31, 31, c1)
	# Let's check that it worked ?
	print(img.get_pixel(31, 31))
	# This gives me a beautiful: 0.698039,0.85098,0.039216,1
	# Amazing let's set the layer then !
	tex3D.set_layer_data(img, 0)
	# Great so then let's see if it worked ? 
	print(tex3D.get_layer_data(0).get_pixel(31, 31))
	# 0,0,0,1
	# What ? O.o 
	print(tex3D.data['layers'][0].get_pixel(31, 31))
	# 0,0,0,1
	# Yup nothing !
	
	# Let's feed the texture to the shader
	mat.set_shader_param("My3DTexture", tex3D)

Edit: I also checked that I wasn’t crazy by trying this on a 2D texture on a Sprite, and this worked just fine, so the pixel setting on an img works along with the texture shader pipeline.

extends Sprite

func _ready():
	var texture = ImageTexture.new()
	var img = Image.new()
	# Let's give it the right dimension 64x64 and the right format
	img.create(64, 64, false, Image.FORMAT_RGB8)
	# Gotta Lock it to set a pixel (this is confusing ... Like, Lock? Why not "Unlock" it ??? Anyhow)
	img.lock()
	# Define out color
	var c1 = Color("#ffb2d90a")
	# And set the pixel
	img.set_pixel(0, 0, c1)
	img.set_pixel(1, 1, c1)
	img.set_pixel(2, 2, c1)
	img.set_pixel(3, 3, c1)
	
	texture.create_from_image(img)
	material.set_shader_param("tex", texture)
:bust_in_silhouette: Reply From: davids91

You are right, print doesn’t work, I could reproduce the problem.
However, if I render the material with a shader, it shows the exact data you set!

shader_type canvas_item;
render_mode unshaded;

uniform sampler3D tex;

void fragment() {
	COLOR = texture(tex, vec3(UV,1));
}