Venus Lua API › Rendering
RenderTexture
Off-screen color + depth target a camera can render into.
lua/venus_lua/src/lua_render_texture.rs
RenderTexture
An off-screen render target — a paired color and depth texture a Camera can draw into
instead of the swapchain. Create one, hand its GPU handles to a camera’s render targets, then sample
its color Texture in a material (e.g. for a mirror, minimap, or picture-in-picture).
Static calls use the type table (RenderTexture.Create(...)); instance methods use colon syntax on
a handle (rt:ColorTexture()).
Types
RenderTexture — an opaque 8-byte handle (userdata) to a render-target pair. A handle may be
null or stale (its target destroyed); it is safe to keep and pass around. tostring(rt)
yields the constant string RenderTexture (it does not embed a name).
Creation & Destruction
RenderTexture.Create( string name, integer width, integer height, integer colorFormat, integer depthFormat, string msaa ) : RenderTexture
Creates a render target named name. colorFormat / depthFormat are still RenderTexture.PixelFormat
integers (e.g. RGBA8 color, D32 depth); msaa is a multisample-level name (see
Constants). Returns the new handle, or nil (logs InvalidArgument) if either format
is unknown or GPU allocation fails. A bad msaa name does not return nil — it raises, because
it is resolved before any allocation happens.
RenderTexture.CreateCube( string name, integer size, integer colorFormat, integer depthFormat ) : RenderTexture
Creates a cube render target named name: a six-face color image (sampled as a TextureCube)
plus one shared face-sized 2D depth image. size is the edge length of each square face, in pixels.
Returns the new handle, or nil (logs InvalidArgument) if either format is unknown or GPU
allocation fails.
Two differences from Create: there is no msaa argument — cube targets are always
single-sample — and there is no separate width/height, because faces are square. rt:Width() and
rt:Height() both report size. Everything else on the handle behaves normally: GpuColorHandle /
GpuDepthHandle / ColorTexture / Destroy / Exists.
Feed it to a render-to-cubemap Camera with
cam:SetCubemapTarget( cube:GpuColorHandle() ) and
cam:SetDepthRenderTarget( cube:GpuDepthHandle() ).
The color attachment must be declared
TextureCubein the shader, neverTexture2D.
cube:ColorTexture()returns an ordinary Texture handle and binds into a material
texture slot like any other, but the underlying image view is a cube view. The consuming HLSL must
declare that slotTextureCube<float4>and sample it with a direction, not a UV. Nothing in
the Lua layer can catch the mismatch — there is noIsCubequery — so a cube bound to a
Texture2Dslot fails at the shader, not at the call.Sample with the raw world-space direction — no axis negation. The capture path is
convention-standard, so an environment reflection isenvCube.Sample(s, reflect(-V, N))and
nothing more. If a result looks mirrored, the fault is in the capture setup, not the lookup.
Known issue — cube targets are single-mip, so there is no blurred / rough sampling. The image
is created with one mip level and without transfer usage, so no mip chain can be generated or
attached.SampleLevelabove LOD 0 has nothing to read, and there is no prefiltered-roughness
path. Only a perfectly mirror-like lookup is available today — a rough or glossy surface has no
correct way to sample this cube.
RenderTexture.Get( string name ) : RenderTexture
Looks up an existing render texture by name. Returns nil if none exists (no warning — a miss is a valid result).
RenderTexture.Exists( RenderTexture rt ) : boolean
Returns true if the handle refers to a live render texture. Safe on null / stale / foreign handles
(returns false, never warns).
rt:Destroy( ) : void
Destroys the render texture and releases its GPU color and depth images. No-op on a null handle.
Functions
Read-only queries on a handle.
rt:GpuColorHandle( ) : integer
The color target’s GPU handle, packed into an integer for a camera’s color render target. Returns
0 (logs NotFound) on error. See “GPU handles” below.
rt:GpuDepthHandle( ) : integer
The depth target’s packed GPU handle, for a camera’s depth render target. Returns 0 (logs
NotFound) on error.
rt:ColorTexture( ) : Texture
The color attachment as a sampleable Texture handle (bind it in a material). nil if
unset, or nil (logs NotFound) on a dead handle.
rt:DepthTexture( ) : Texture
The depth attachment as a Texture handle. nil if unset, or nil (logs NotFound) on
a dead handle.
rt:Width( ) : integer
Width in pixels. Returns 0 (not nil, logs NotFound) on error.
rt:Height( ) : integer
Height in pixels. Returns 0 on error.
GPU handles
GpuColorHandle / GpuDepthHandle return a GpuHandle<GpuRenderTarget> reinterpreted as a raw
u32 integer (a 4-byte handle, layout [gen:8 | pool_type:8 | index:16]). Treat it as opaque:
do not do arithmetic on it — just pass it straight to camera:SetRenderTarget(...) /
camera:SetDepthRenderTarget(...), which transmute the same integer back into a handle. 0 is the
null handle and is also the error return, so a failed lookup passes through harmlessly as “no
target.”
Constants
RenderTexture.PixelFormat — pixel format discriminants for colorFormat / depthFormat. An
unrecognized value fails Create with InvalidArgument.
None (0), BGRA8_sRGB (1), R8 (2), RG8 (3), R16F (4), BGRA8 (5), RG16F (6),
R32F (7), RGBA16F (8), RG32F (9), D32 (10), D32_S8 (11), BC1 (12), BC1_sRGB (13),
BC2 (14), BC2_sRGB (15), BC3 (16), BC3_sRGB (17), BC4 (18), BC5 (19), BC6 (20),
BC7 (21), BC7_sRGB (22), RGBA8 (23), RGBA8_sRGB (24).
The multisample level for msaa is a name, matched exactly with no case folding — one of
"None", "X2", "X4", "X8", "X16". An unrecognized value raises a Lua error listing all
five. The name is deliberately not the sample count: "4" is rejected, because the engine
numbers these levels 1/2/4/8/16 while the settings layer numbers the same five 0..4, and a bare
integer is ambiguous between the two.
The
RenderTexture.MSAALevelsub-table was deleted (2026-07), soRenderTexture.MSAALevel
is nownil. It had only ever exposedNone/X4/X8—X2andX16were unreachable from
Lua, andCreatematched only1/4/8, so asking for 2 or 16 samples silently produced
none. Both halves are gone; all five levels are now reachable and anything else raises.
Example
-- Render a second camera into a 1024x1024 target and show it on a UI quad.
-- (Adapted from data/scripts/main_scene_setup.lua.)
local PF = RenderTexture.PixelFormat
local rt = RenderTexture.Create("mini_camera", 1024, 1024, PF.RGBA8, PF.D32, "None")
if rt then
-- Packed GPU handles go straight to the camera's render targets.
cam:SetRenderTarget(rt:GpuColorHandle())
cam:SetDepthRenderTarget(rt:GpuDepthHandle())
-- The color attachment is a sampleable Texture for a material.
rt_material:SetTexture(0, rt:ColorTexture())
end
-- A cube target for an environment capture: square faces, no msaa argument.
local cube = RenderTexture.CreateCube("env_probe", 256, PF.RGBA16F, PF.D32)
if cube then
cam:SetCubemapTarget(cube:GpuColorHandle()) -- see Camera
cam:SetDepthRenderTarget(cube:GpuDepthHandle()) -- required
env_material:SetTexture(0, cube:ColorTexture()) -- shader slot must be TextureCube
end