I am making a 3D game using 2D sprite assets. The way the sprites are implemented into the 3D engine is by using a SubViewport texture as the albedo of a material on a PlaneMesh MeshInstance3D. The node structure looks something like this:
–MeshInstance3D
–^-SubViewport
----^-Sprite2D
Some Sprite2Ds have a canvas_item shader applied. For the most part, this works pretty well for me.
I can also use this same mesh instance for a multi-mesh, this also works pretty well. Here’s what that looks like with some simple grass sprites:
While I do like how this looks, what I would love is the ability to create wind effects by offsetting each sprite instance’s shader so they don’t all move at the same time.
What doesn’t appear possible is the ability to configure the canvas_item shader of the Sprite2D underneath the multimesh instance, say, through multimesh.set_instance_custom_data(). I assume this is because the shader with access to INSTANCE_CUSTOM would be the one applied to the mesh itself, not the underlying Sprite2D.
Is there any way to configure each Sprite2D shader in a multimesh?
The problem is interesting, I think the root of it is the way you implemented the 2d to 3d. Using a subviewport seems like a hack, the “proper” way to me would’ve been to write a spatial shader which renders the given texture. You would then implement the wind sway in the spatial shader. Then you could use the INSTANCE_CUSTOM data to pass custom wind values to individual grasses.
Shaders are still quite new to me, so it seems I have some homework to do in order to achieve these effects using only a spacial shader. I will look into it and post here if I find the solution.
That is generaly done by offsettig the sprites movement animation by it’s world posiiton. that way the animation will be different at ddifferent world points, you can either have it be smooth ripples flowing though a field (like you see sometimes on big grassfields when it;ls very windy) or you can combine the world position and a noise texture to make it more chaotic.
I saw a tutorial on it but I have been watching so many I can;t find it again. Will edit this post if I find it.
also I’d add some ‘global uniform’ variables to your shader, you need to set them up inside the project as ‘shader globals’ (under project settings) but then you could adjust things like wind-speed from a script and have it apply to everything using that variable.
Here’s some documentation on that: Godot 4.0 gets global and per-instance shader uniforms
If I could access the global position of the 3D world, I would definitely use it. However, if I understand correctly, the canvas_item shader is looking at the 2D space to determine the position, and for each mesh instance that it gets drawn on, it will always be x=0 and y=0, effectively.
Unless there is some way I don’t yet know!
The current plan is to convert it over to a spacial shader, and ditch the Sprite2D node and canvas_item shader. As soon as I figure that out, I will most likely be utilizing your advice.
I was able to get this to work with some direction from peltodev.
Some tutorials online suggested I do the method found in the original post. I had some difficulties setting it up via script so I can’t recommend it unless you’re building each scene with this method.
If you get as far as I did, the way to get started converting it over to a spatial shader is to use this handy “Convert to ShaderMaterial” option in the mesh → material menu.
From there godot was able to build a shader for me that handled the billboarding and other configurations related to the standard 3D material.
The only other task was to convert over the canvas_item shader I had to the newly created spatial shader. It took a bit of learning, but I did get it to work!