Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | Alikae |
##Project Description
I’m trying to generate a texture via GDScript to use it (as an heightmap) in a shader.
The only way I found to do this was to use a Viewport containing a Sprite, being able to update my ‘texture’ by overwriting Sprite._draw() and to get the result via viewport.get_texture().get_data().
I do my initialization of the Sprite in its first _draw call, here just drawing a black background. Then at every subsequent call I retrieve back the drawn sprite on the viewport, redraw it, and update it by redrawing on top of it.
##Problem
I use a Timer to update the Sprite every .05s, but if I start it in _ready with a wait_time of .05 the first_pass in _draw seems to be discarded, and the black background is not rendered.
My guess would be that the second _draw call is made before the first finish, retrieving a non-initialized image.
Setting the wait_time to .2s the first time seems to solve the problem, but also seems a very bad solution.
My scene tree look like that:
The script inside the Sprite:
extends Sprite
onready var timer = $RedrawTimer
# Needed to avoid Godot freeing them too soon by lack of reference
var tex = null
var old_image = null
# Just used to distinguish the first _draw call,
# because I need to initialize the texture by code and
# can't find how to do it outside of _draw.
var first_pass = true
func _ready():
# There is the problem.
# Wait .2s at the first pass or the first draw may be discarded (probably a race condition)
timer.wait_time = 0.2
timer.connect("timeout", self, '_on_timer_timeout')
timer.start()
func _on_timer_timeout():
# Set back the timer cooldown to its normal value
timer.wait_time = 0.05
update()
func _draw():
if (first_pass):
# Draw black background
draw_rect(Rect2(Vector2(0, 0), Vector2(200, 200)), Color(0.0, 0.0, 0.0))
first_pass = false
else:
# Take the previous frame image to update it
old_image = Image.new()
old_image.copy_from(get_viewport().get_texture().get_data())
old_image.flip_y() # flip it back. due to the way OpenGL works, the resulting ViewportTexture is flipped vertically.
# Draw it back
tex = ImageTexture.new()
tex.create_from_image(old_image)
draw_texture(tex, Vector2(0, 0))
# Draw some modifications
draw_circle(Vector2(50, 70), 10, Color(0.0, 0.0, 1.0, 0.02))
# Draw a white outline
draw_rect(Rect2(Vector2(1, 0), Vector2(200, 200)), Color(1.0, 1.0, 1.0), false)
I would like to know if my guess of a race condition is correct, and how to solve it.
Any advice or other method I can use to generate by script a texture usable by shaders would also be welcomed.
Did you tried to to set default texture of sprite to black sprite in the editor? I mean to make black square by yourself as .png image and set it as texture
in sprite’s properties? This way, first image should always be black.
You can also use shader’s params (uniform
s) to pass data from gdscript to shader. For example, sampler2D
can be used for binding 2D textures from gdscript to shader. To set shader’s param from gdscript, you need to use set_shader_param
function (ShaderMaterial — Godot Engine (3.4) documentation in English).
AlexTheRegent | 2022-03-18 07:39
As I want to procedurally generate the first image, using a png would mean generate it and it seems hacky and counter productive (Would work nicely with a static first image, though).
I’m already using uniform
s, what i’m trying to do is initialize and update properly the texture sent to the GC via set_shader_param
.
Alikae | 2022-03-18 16:24