Drawing rects with custom shader
Asked Answered
D

14

0

I need to draw a bunch of rectangles (possibly hundreds) with a custom shader, but each rectangle needs to have different uniforms set in the shader. As per documentation I tried using _draw() on a Node2D and the rects are indeed drawn with the shader. However it seems that uniforms cannot be changed between draw calls as all my rects are just drawn with same uniforms values. This is what I'm doing:

func _draw():
    	set_material(material)
    	material.set_shader_param("a",  1)
    	draw_rect(Rect2(0.0, 0.0, 200.0, 200.0), Color(1.0, 1.0, 1.0, 1.0))
    	material.set_shader_param("a",  2)
    	draw_rect(Rect2(200.0, 200.0, 200.0, 200.0), Color(1.0, 1.0, 1.0, 1.0))

Both rects are drawn with value of the uniform "a" set to 2. I'm aware it may not be an optimal approach. Is there a "proper" way to handle this?

Dowser answered 6/9, 2021 at 5:7 Comment(0)
B
0

That's because the drawing happens only once at the end afaik.

Bowra answered 6/9, 2021 at 8:26 Comment(0)
D
0

Yep, I figured that out. But is there any other way to do this? How can I use the same shader on different 2d object with differently set uniforms? Can it be done with sprites, or instancing, or something else?

Dowser answered 6/9, 2021 at 14:19 Comment(0)
B
0

Might need to create a new copy of the material and apply to a separate object.

Bowra answered 6/9, 2021 at 15:30 Comment(0)
D
0

I have several hundred objects. Making that many copies of the same material/shader sounds like an overkill. And I'd still need a node for each object. This defeats the purpose of using custom draw in the first place.

Dowser answered 6/9, 2021 at 16:16 Comment(0)
B
0

Well it's all up to you needing different uniforms for each of them.

Bowra answered 6/9, 2021 at 16:50 Comment(0)
D
0

Shouldn't this be possible at least with particles. I'm not very experienced with Godot, so I figured I should ask.

Dowser answered 6/9, 2021 at 16:53 Comment(0)
B
0

Hmm, this might be a XY problem. Maybe explain what your ultimate goal is?

Bowra answered 6/9, 2021 at 17:31 Comment(0)
D
0

@Megalomaniak said: Hmm, this might be a XY problem

Sure, it may well be:) Believe it or not that crossed my mind. I just didn't want to ask too complicated question right off the bat.

I want to texture a sphere with circles. Like a polka dot pattern only with varying size and placement of circles. These circles are gameplay elements and their position and size can change each frame. And as I said, there can be hundreds of them.

In order for this kind of texture to be mapped onto a sphere without deformations or pinching, u and v in the texture must represent radial coordinates on the sphere. Because of this it's not enough to draw plain circles into the viewport/texture. Circles in the texture need to be represented in spherical space. This representation is position dependent. So the shader that draws circles into the texture would require position information (per circle) in order to produce proper representation of each circle in radial coordinates.

Hopefully I explained it clearly. If not, I can post some visuals.

EDIT: Here's an image to better illustrate the thing. Upper left is the texture that is mapped onto the sphere. Note how circle in this texture is always nonlinearly deformed depending on its position in radial space. This ensures that circle is never deformed or its size changed when viewed on the sphere, regardless of its position.

Dowser answered 6/9, 2021 at 17:47 Comment(0)
B
0

Perhaps you should consider decals? Here's a plugin that might be of interest: https://github.com/Master-J/DecalCo

Bowra answered 6/9, 2021 at 20:57 Comment(0)
D
0

Thanks. I'll try this out. By the looks of it there'll still be a spatial node per decal but at least they can share the same shader. We'll see how efficient it is with a large number of decals...

Dowser answered 6/9, 2021 at 21:37 Comment(0)
B
0

Yeah, I have not benchmarked this at all. Results could be quite interesting.

Bowra answered 7/9, 2021 at 1:13 Comment(0)
D
0

Toyed with decals a bit. The shader is good, I'll definitely keep it in the pocket. But still not adequate for my needs. I have some additional geometry on the surface of the sphere. Since decals project on everything, they'll leave projection artefacts on that geometry.

I pushed forward with my initial intuition on drawing rectangles into a texture. And managed to haxor it. The shader needs three floats per circle; uv of the center and a radius. I'm sending them via the color argument in draw_rect() call. Even managed to send an actual color by packing it into the remaining float.

I'm guessing this can further be optimized by using a single draw_primitive() call to draw all of the rectangles at once, as to avoid excessive iterating in the script.

Tested the thing with couple hundred animated circles and it runs smoothly.

Dowser answered 7/9, 2021 at 5:43 Comment(0)
D
0

I bumped into further performance problems with this setup, concerning viewport and custom drawing. It's similar to what was discussed in this thread so I'll continue there

Dowser answered 7/9, 2021 at 18:52 Comment(0)
D
0

Finally found an acceptable solution. I ditched custom draws as they were just too slow and instead used MultiMeshInstance2D with quad geometry. Instances accommodate sending custom color and four more floats to the shader per instance. That way I can send all needed information for a circle. There was a little bit of math involved in finding a minimal bounding box of each deformed circle in the texture space so the quads can be positioned and sized properly to avoid processing excess pixels.

Here is 1500 animated circles running at very comfortable frame rates:

Dowser answered 10/9, 2021 at 16:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.