How to render to a manually created Viewport?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By bluenote

I’m trying to dynamically create a Viewport, render to it, fetch its contents (as a texture), and discard it again. As a basic test I’m using this script attached to a plain Node2D root node (nothing else in the scene tree):

extends Node2D

func _ready():
    # Create viewport
    var viewport = Viewport.new()
    viewport.size = Vector2(200, 200)
    
    # Create some test content
    var rect = ColorRect.new()
    rect.color = Color(1, 0, 0)
    rect.rect_size = Vector2(100, 100)
    
    # Add to scene
    viewport.add_child(rect)
    add_child(viewport)

    # Wait for content
    yield(get_tree(), "idle_frame")
    yield(get_tree(), "idle_frame")
    
    # Fetch viewport content
    var texture = viewport.get_texture()
    var image = texture.get_data()
    image.save_png("test.png") # just as a debugging check

This works in the sense that the I do get a “test.png” output file. However, the file is just black – it looks like nothing has been rendered to the viewport. If I add the test content (the rect) directly as a child it renders fine.

What am I missing that I’m not getting any output from the viewport?

maybe you could try

#Add to scene
viewport.add_child(rect)
rect.show()
add_child(viewport)

Klagsam | 2019-12-14 09:22

@Klagsam Thanks for the suggestion. Unfortunately that doesn’t seem to make a difference for me. Does it work for you?

bluenote | 2019-12-14 10:12

I did not try it. I just had a similar problem which i could solve by adding that line.

Klagsam | 2019-12-14 11:20

:bust_in_silhouette: Reply From: Xtremezero

you need to add one of the following lines after creating your viewport variable

viewport.render_target_update_mode =Viewport.UPDATE_ONCE

or

viewport.render_target_update_mode =Viewport.UPDATE_ALWAYS

(before edit)
I believe you need to add a Camera2D as well beneath your Viewport

as each viewport renders from the child camera beneath it (am not 100% sure tho)

I tried that as well, but it doesn’t make a difference. Also, I’ve seen many (working) examples of viewports without explicit sub-cameras. My understanding is that they come with an implicit camera (much like the root viewport), but if there is an explicit camera that one is preferred.

bluenote | 2019-12-14 20:26

omg I was so idiot :confused: you need to change the update_mode

viewport.render_target_update_mode =Viewport.UPDATE_ONCE
or
viewport.render_target_update_mode =Viewport.UPDATE_ALWAYS

extends Node2D

func _ready():
	 # Create viewport
	var viewport = Viewport.new()
	viewport.size = Vector2(200, 200)
	viewport.render_target_update_mode =Viewport.UPDATE_ALWAYS
	#var cam = Camera2D.new()
	#viewport.add_child(cam) 

    # Create some test content
	var rect = ColorRect.new()
	rect.color = Color(1, 0, 0)
	rect.rect_size = Vector2(100, 100)

    # Add to scene
	viewport.add_child(rect)
	add_child(viewport)

    # Wait for content
	yield(get_tree(), "idle_frame")
	yield(get_tree(), "idle_frame")

    # Fetch viewport content
	var texture = viewport.get_texture()
	var image = texture.get_data()
	image.save_png("test.png") # just as a debugging check

Xtremezero | 2019-12-15 02:33

That’s it, indeed the default of UPDATE_WHEN_VISIBLE implies that the viewport is simply not active. Thanks a lot!

bluenote | 2019-12-15 09:07