• 2D
  • Changing material in custom draw call

I'm trying to do some custom rendering. I draw a background, then punch holes through it using by drawing with BLEND_MODE_SUB. I got this working in GameMaker relatively easily, but I can't get it to work in Godot.

I am using a CanvasItemMaterial for my Node2D, but only the final blend_mode I set on it has any effect, probably because Godot is caching the draw calls. Is there a way to basically force Godot to render the current draw calls, then continue on my way? Or is there a better way of doing things?

Code:

extends Node2D

var _material: CanvasItemMaterial = load("res://materials/MaterialBlendSub.tres");

export (Texture) var LightTexture;

# Called when the node enters the scene tree for the first time.
func _ready():
	material = _material;

func _draw():
	material.blend_mode = BLEND_MODE_MIX;
	self.draw_rect(Rect2(0, 0, 500, 500), global.COLOR_BLACK);
	
	material.blend_mode = BLEND_MODE_SUB;
	for child in self.get_children():
		var transform: Transform2D = child.transform;
		draw_set_transform_matrix(transform);
		self.draw_texture(LightTexture, Vector2(0,0));
	draw_set_transform_matrix(self.transform);

EDIT: I want to do something similar to what's described in this post, but I didn't see a satisfactory answer there, and I'm not inclined to necro-post like that.

@bindernews said: I'm trying to do some custom rendering. I draw a background, then punch holes through it using by drawing with BLEND_MODE_SUB. I got this working in GameMaker relatively easily, but I can't get it to work in Godot.

I am using a CanvasItemMaterial for my Node2D, but only the final blend_mode I set on it has any effect, probably because Godot is caching the draw calls. Is there a way to basically force Godot to render the current draw calls, then continue on my way? Or is there a better way of doing things?

If I recall correctly, you need to call update() to trigger a new draw call for the node. The documentation page on custom drawing in 2D explains a bit more.

EDIT: I want to do something similar to what's described in this post, but I didn't see a satisfactory answer there, and I'm not inclined to necro-post like that.

We do not have any rules against necro-posting here on the forums, though there is also nothing wrong with making a new post either. Whatever works best for you should be fine :smile:

As for doing what is described in that post, I think we were not able to find a viable solution.

So after playing around with it some more, I figured out a "solution". Have a child node that draws the lights with its own material. Apparently each node can only have one material/shader, so changing the drawing mode requires having a sub-node with the new draw mode. It's not quite as easy as Game Maker, but it's not too bad either. The part about having to have that on a viewport and have the viewport in a ViewportContainer is annoying, but I can forgive that. Regardless it's not too bad, just more (and slightly differently) annoying. I currently have one large test project, so I'll slim it down to a demo project and post it later for anyone who wants to achieve the same effect.

EDIT: Having trouble getting it working with a camera. I'll post again once I get it fixed.

Here's the demo. You can drag the camera around in the editor and see how the lights move with it in the darkness viewport. To make things where the lights are dependent on the world coordinates, you'd have to move the darkness Node2D to be transformed opposite of the camera. There's probably a bit more to do as well, regarding translating lights and making sure to always draw the black overlay correctly, but I'll leave that as an exercise to the reader. Or if anyone wants me to I'll go back and update the demo to work like that.