How to take a "screenshot" of the game and use it as a texture on a sprite?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Brynjar Harðarson
:warning: Old Version Published before Godot 3 was released.

So far I’ve been using sprite.set_texture(get_viewport().get_texture()) and it kind of works, but once I add the sprite to the game using add_child(sprite) it is placed at the top, over all my other sprites, while I want it to be below them all, so I set .z on my sprites to 1 and now it’s below, but for some reason the generated texture/screenshot only includes graphics that are at or below the sprite receiving the screenshot, everything above gets excluded, which I find strange since get_viewport().get_texture() should work independently from the sprite receiving it, if I bump its z-index to 1 as well the sprites show up again in the screenshot, but again, the sprites are placed below the screenshot sprite instead of over it, which is not desired.

Here is my code:

extends Node2D

var screen = Sprite.new()

func _ready():
	var texture = get_viewport().get_texture()
	screen.centered = true
	screen.flip_v = true
	screen.z = 0
	screen.set_texture(texture)
	add_child(screen)

func _process(delta):
	var texture = get_viewport().get_texture()
	screen.set_texture(texture)

If I try to place the sprites on top not with z-index but using an add_child order the same thing happens.

One idea I have is that maybe there is some rendering pipeline that renders the sprites in the order of the z-index, from bottom to top, and the screenshot is executed not at the end of this process but at the point when the sprite receiving the screenshot gets rendered, and so anything rendered after that (items with a greater z-index) don’t get included.

Any ideas?

P.S.
I’m new to Godot, and I’m using Godot 3 Beta 2.

:bust_in_silhouette: Reply From: Sylvain22

Hi,

Very old question without answer. I’m answering for Godot 3.2.1, taking a screenshot, which become assignable to a Sprite. I don’t know the trick with the z-index, sorry.

There is an example of code in the Templates (you can load it from the project manager):

Screen Capture Demo

func take_screenshot():
	var old_clear_mode =get_viewport().get_clear_mode()
	get_viewport().set_clear_mode(Viewport.CLEAR_MODE_ONLY_NEXT_FRAME)
	# Let two frames pass to make sure the screen was captured.
	yield(get_tree(), "idle_frame")
	yield(get_tree(), "idle_frame")

	# Retrieve the captured image.
	var img = get_viewport().get_texture().get_data()
    # restore the previous value, as some part wont redraw after...
	get_viewport().set_clear_mode(old_clear_mode)

	# Flip it on the y-axis (because it's flipped).
	img.flip_y()

   # I also want a thumbnail 1/3 size
	var s = img.get_size()
	var scale_reduce = 0.3
	img.resize(s.x * scale_reduce, s.y * scale_reduce)
	
    # I crop it to my need 
	var border = get_parent().get_node("border")
	var border_s = border.to_global(border.texture.get_size())
	var border_p = border.to_global(border.position)
	var zone = Rect2(Vector2(0,0), border_s * scale_reduce)
    
    # copy to a new image, I need to create it first with the same size with create()
	var imgDest = Image.new()
	imgDest.create(zone.size.x, zone.size.y, false, img.get_format())
	imgDest.blit_rect(img, zone, Vector2.ZERO)
	
	# Create a texture for it.
	var tex = ImageTexture.new()
	tex.create_from_image(imgDest)

    # the texture can be assigned to a Sprite2D or a TextureRect
	$mini.texture = tex

Hope that helps.
Regards,
Sylvain.

Godot 4 update:

var img = get_viewport().get_texture().get_data()
img.flip_y()

becomes

var img = get_viewport().get_texture().get_image()

(no need to flip y anymore; maybe later, if 4.1 supports OpenGL again and you use that, you may need to)

Hyper Sonic | 2022-11-06 17:43