Godot Version
3.6, 4.3
Question
a working hiresolution screenshot renderer
working on 3d setup root Spatial, Camera, Mesh Instance with shaders, a node with the following script attached. well camera settings not working so well per tile calc, but all debug steps are coded in, should be easy to follow along, any leads for frustum calcs of tile sections from current viewport, lets make a working resolution screenshot renderer for godot programs. this comes from a handy modified Processing TileSaber we use all the time to render hires images of final viewport outputs.
would be nice to extend to 2d as well.
could you help upgrading this script?
extends Spatial
export var final_width = 3840 # Final image width
export var final_height = 2160 # Final image height
export var viewport_width = 1920 # Viewport width
export var viewport_height = 1080 # Viewport height
export var tile_num = 2 # Number of tiles in each dimension
var viewport: Viewport
var camera: Camera
var result_image: Image
var viewport_display: TextureRect
func _ready():
setup_viewport()
setup_viewport_display()
call_deferred("capture_high_res_image")
func setup_viewport():
viewport = Viewport.new()
viewport.size = Vector2(viewport_width, viewport_height)
viewport.usage = Viewport.USAGE_3D
viewport.render_target_update_mode = Viewport.UPDATE_ALWAYS
viewport.transparent_bg = false
camera = Camera.new()
camera.current = true
viewport.add_child(camera)
# Copy the main camera's transform to our new camera
var main_camera = get_viewport().get_camera()
if main_camera:
camera.global_transform = main_camera.global_transform
camera.fov = main_camera.fov
camera.near = main_camera.near
camera.far = main_camera.far
print("copied the main camera's transform to our new camera")
# Add your MeshInstance to the viewport
var mesh_instance = get_node("../MeshInstance")
if mesh_instance:
var mesh_copy = mesh_instance.duplicate()
viewport.add_child(mesh_copy)
print("copied the main camera's transform to our new camera")
add_child(viewport)
func setup_viewport_display():
viewport_display = TextureRect.new()
viewport_display.rect_size = Vector2(320, 180)
viewport_display.expand = true
viewport_display.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
var canvas_layer = CanvasLayer.new()
canvas_layer.add_child(viewport_display)
add_child(canvas_layer)
func capture_high_res_image():
result_image = Image.new()
result_image.create(final_width, final_height, false, Image.FORMAT_RGB8)
var progress_label = Label.new()
progress_label.rect_position = Vector2(10, 200)
add_child(progress_label)
var aspect = float(viewport_width) / viewport_height
var fov_y = deg2rad(camera.fov)
var fov_x = 2 * atan(tan(fov_y / 2) * aspect)
for tile_y in range(tile_num):
for tile_x in range(tile_num):
progress_label.text = "Capturing tile %d,%d of %d,%d" % [tile_x+1, tile_y+1, tile_num, tile_num]
for ynum in range(10):
yield(get_tree(), "idle_frame")
setup_camera_for_tile(tile_x, tile_y, fov_x, fov_y)
viewport.render_target_update_mode = Viewport.UPDATE_ONCE
yield(get_tree(), "idle_frame")
yield(get_tree(), "idle_frame")
var viewport_texture = viewport.get_texture()
var viewport_image = viewport_texture.get_data()
viewport_image.convert(Image.FORMAT_RGB8)
viewport_image.flip_y()
viewport_image.save_png("res://debug_tile_%d_%d.png" % [tile_x, tile_y])
var target_x = tile_x * viewport_width
var target_y = tile_y * viewport_height
result_image.blit_rect(viewport_image,
Rect2(Vector2.ZERO, viewport.size),
Vector2(target_x, target_y))
restore_camera()
result_image.save_png("res://high_res_image.png")
print("High-resolution image saved!")
remove_child(progress_label)
progress_label.queue_free()
func setup_camera_for_tile(tile_x, tile_y, fov_x, fov_y):
var near = camera.near
var far = camera.far
var tile_width = tan(fov_x / 2) * 2 * near
var tile_height = tan(fov_y / 2) * 2 * near
var left = -tile_width / 2 + tile_width * (float(tile_x) / tile_num)
var right = -tile_width / 2 + tile_width * ((tile_x + 1.0) / tile_num)
var bottom = tile_height / 2 - tile_height * ((tile_y + 1.0) / tile_num)
var top = tile_height / 2 - tile_height * (float(tile_y) / tile_num)
camera.set_frustum(1, Vector2(left, top), near, far)
camera.set_frustum(0.01, Vector2(left, top), near, far)
camera.set_frustum(0.055, Vector2(left, top), near, far)
# void set_frustum(size: float, offset: Vector2, z_near: float, z_far: float)
#
#Sets the camera projection to frustum mode (see PROJECTION_FRUSTUM), by specifying a size, an offset, and the z_near and z_far clip planes in world space units. See also frustum_offset.
func restore_camera():
var main_camera = get_viewport().get_camera()
if main_camera:
camera.fov = main_camera.fov
camera.near = main_camera.near
camera.far = main_camera.far
func _process(delta):
viewport.render_target_update_mode = Viewport.UPDATE_ONCE
viewport_display.texture = viewport.get_texture()
if(Input.is_key_pressed(KEY_0)):
capture_high_res_image() # esta a criar varios objectos