Ways to mimic per-instance uniforms for Canvas Item shaders?

Godot 4.3 beta1

I’ve been working on some alternate ways of rendering tilemaps lately, and one of the methods I’ve had some success with so far has been generating ArrayMeshes for the tiles. However, to achieve the effect that I want, I need to pass extra data about the tile and its neighbors. There are no actual per-instance uniforms for Canvas Items though, so I’ve tried some workarounds like storing my custom data in the UV and COLOR attributes of each mesh vertex.

I’ve been able to compress my data to about 18 bytes by shaving off some unneeded bits from each value, but unfortunately that’s not enough. I thought between UV and COLOR I’d have 8+16=24 bytes total. To my dismay, the Godot cpp code converts the Color I pass from floats to uints, which both destroys the data I had stored there and reduces the potential capacity to just 8+4=12 bytes. If this format conversion didn’t happen I’d be fine, but it seems mesh vertices must have color defined using 8 bit values.

Has anyone else figured out any clever ways to pass per-instance data to Canvas Item shaders? I’m not sure what other attributes I could try and use to pass my data.

As a last resort I’ve considered writing all of this data to an image and assigning that as a normal shader uniform, and instead passing coordinates into that image to look up the needed information. But I’m also a tad worried that this would be a bigger performance hit than just passing what I need directly, as right now I’m calculating what I need as flat varyings in the vertex shader, and I’d need to do these lookups in the fragment shader.

1 Like

Per instance uniform for 2D is something that has been asked a lot, and I believe some form of solution will eventually arrivedl. Not by 4.3 for sure. I don’t know when. But if you’re willing to help with that, there’s some work that has been done but needs more iterations and tests.

I was able to mimic it in 4.3 by creating a new instance of the material when the scene is created:

func _ready() -> void:
	var shader_material = ShaderMaterial.new()
	var shader = load("res://your_shader.gdshader")
	shader_material.set_shader(shader)
	material = shader_material