I'm struggling to figure out RenderingServer calls on a 2D game in Godot 4.2. I've exhausted Google and the documentation, so I'm hoping the community can help.
What I'm trying to achieve
Given there is currently no depth buffer in 2D, I'm creating it myself. This involves rendering the world twice, setting a global variable RENDER_PASS
as 0
for the depth pass and 1
for the game view. Each sprite has a custom shader; if global uniform RENDER_PASS
is 0
, the shader renders the sprite in a solid color based on the sprite's worldY position; if RENDER_PASS
is 1
then the sprite is rendered in color as normal. The output of render pass 0
should be a grayscale image representing depth.
I first attempted this with viewport nodes in the tree, and it worked albeit flickering between the two frames. So I know the creation of the depth image works as intended, but I need manual control over the rendering to perform both renders within the same frame, hence a migration to RenderingServer
. Here's what the depth frame looked like (the black is a TileMap) :-
![](https://godotforums.org/assets/files/2023-12-07/1701938306-813710-image.png)
As a side note, I need the depth texture to compare position of lights within the scene for shadow calculation, after hitting the (Vulkan) limit of lights per surface with Godot's Light2D (even after increasing it).
What I'm struggling with
I can't determine which RenderingServer commands to use, and whether I need to copy world2D and global transforms over to the new viewports. Right now the viewports aren't rendering anything; the game just runs behind a black screen. I'm also not convinced I can pass the RID of the depthTexture directly into the shader even though the documentation suggests I can. All funcs are being entered as and when expected.
The solution avoids copying data from the GPU to CPU per frame to avoid killing performance. Hence the use of RIDs and not copying texture data.
Code
# non-ready vars
var camera: Camera2D
var viewportDepth: RID
var viewportGame: RID
var depthTexture: RID
var viewSize: Vector2i
var isInitialized: bool
static var _instance: RenderManager = null
static var instance: RenderManager :
get:
return _instance
func _ready():
Debug.logInfo("RenderManager::_ready()")
_instance = self if _instance == null else _instance
viewSize = get_viewport().size # which is 3840x2054
func _process(delta: float) -> void:
if not isInitialized:
return
RenderingServer.call_on_render_thread(onRender)
func initialize() -> void:
camera = WorldManager.instance.ui.camera
viewportDepth = RenderingServer.viewport_create()
RenderingServer.viewport_attach_camera(viewportDepth, camera)
RenderingServer.viewport_set_size(viewportDepth, viewSize.x, viewSize.y)
RenderingServer.viewport_set_clear_mode(viewportDepth, RenderingServer.VIEWPORT_CLEAR_ALWAYS)
RenderingServer.viewport_set_update_mode(viewportDepth, RenderingServer.VIEWPORT_UPDATE_ALWAYS)
viewportGame = RenderingServer.viewport_create()
RenderingServer.viewport_attach_camera(viewportGame, camera)
RenderingServer.viewport_set_size(viewportGame, viewSize.x, viewSize.y)
RenderingServer.viewport_set_clear_mode(viewportGame, RenderingServer.VIEWPORT_CLEAR_ALWAYS)
RenderingServer.viewport_set_update_mode(viewportGame, RenderingServer.VIEWPORT_UPDATE_ALWAYS)
RenderingServer.viewport_set_render_direct_to_screen(viewportGame, true)
RenderingServer.viewport_set_active(viewportGame, true)
RenderingServer.render_loop_enabled = false
isInitialized = true
func onRender() -> void:
RenderingServer.global_shader_parameter_set("RENDER_PASS", 0)
RenderingServer.viewport_set_active(viewportDepth, true)
# RenderingServer.viewport_set_active(viewportGame, false)
RenderingServer.force_draw(false)
depthTexture = RenderingServer.viewport_get_texture(viewportDepth) # passed into shader elsewhere as set_shader_parameter("depth_texture", RenderManager.instance.depthTexture)
RenderingServer.global_shader_parameter_set("RENDER_PASS", 1)
RenderingServer.viewport_set_active(viewportDepth, false)
# RenderingServer.viewport_set_active(viewportGame, true)
RenderingServer.force_draw(false)
RenderingServer.viewport_attach_to_screen(viewportGame, Rect2(0, 0, viewSize.x, viewSize.y))