Sedulous uses a forward PBR renderer with a multi-pass pipeline. RenderSubsystem handles scene rendering and is automatically registered when you use EngineApplication.
Every scene needs at least one active camera to render. Cameras are components on entities – the entity’s transform determines the camera’s position and orientation.
let cameraEntity = scene.CreateEntity("Camera");
scene.SetLocalTransform(cameraEntity, Transform.CreateLookAt(
.(0, 5, 10), .(0, 0, 0)
));
let cameraMgr = scene.GetModule<CameraComponentManager>();
if (let cam = cameraMgr.Get(cameraMgr.CreateComponent(cameraEntity)))
{
cam.IsActive = true;
cam.FieldOfView = 60.0f;
cam.NearPlane = 0.1f;
cam.FarPlane = 1000.0f;
}
Only one camera can be active at a time. The active camera is used for the main view render.
Three light types are supported:
Simulates sunlight – parallel rays with no falloff. The entity’s orientation determines the light direction.
let sun = scene.CreateEntity("Sun");
scene.SetLocalTransform(sun, .() {
Position = .Zero,
Rotation = Quaternion.CreateFromYawPitchRoll(0.3f, -0.8f, 0),
Scale = .One
});
let lightMgr = scene.GetModule<LightComponentManager>();
if (let light = lightMgr.Get(lightMgr.CreateComponent(sun)))
{
light.Type = .Directional;
light.Color = .(1, 0.95f, 0.9f);
light.Intensity = 2.0f;
light.CastsShadows = true;
light.ShadowBias = 0.002f;
}
Emits light in all directions from the entity’s position, with distance attenuation.
light.Type = .Point;
light.Range = 10.0f;
light.Intensity = 5.0f;
Directional cone of light from the entity’s position along its forward axis.
light.Type = .Spot;
light.Range = 15.0f;
light.InnerConeAngle = 20.0f;
light.OuterConeAngle = 35.0f;
Static meshes are the primary way to display 3D geometry. Attach a MeshComponent to an entity and set its mesh resource reference.
let meshMgr = scene.GetModule<MeshComponentManager>();
if (let mesh = meshMgr.Get(meshMgr.CreateComponent(entity)))
{
var meshRef = ResourceRef(.Empty, "project://models/building.mesh");
defer meshRef.Dispose();
mesh.SetMeshRef(meshRef);
// Optionally set per-slot materials
var matRef = ResourceRef(.Empty, "project://materials/brick.material");
defer matRef.Dispose();
mesh.SetMaterialRef(0, matRef);
}
For animated characters, use SkinnedMeshComponent with a skeleton and animation:
let skinnedMgr = scene.GetModule<SkinnedMeshComponentManager>();
if (let mesh = skinnedMgr.Get(skinnedMgr.CreateComponent(entity)))
{
var meshRef = ResourceRef(.Empty, "project://models/character.mesh");
defer meshRef.Dispose();
mesh.SetMeshRef(meshRef);
}
Materials use a PBR metallic-roughness workflow. The default shader (forward) supports:
| Property | Type | Description |
|---|---|---|
BaseColor |
Color | Albedo color multiplied with texture |
Roughness |
Float | Surface roughness (0 = mirror, 1 = diffuse) |
Metallic |
Float | Metalness (0 = dielectric, 1 = metal) |
AlphaCutoff |
Float | Alpha test threshold (for masked materials) |
AlbedoMap |
Texture | Base color texture |
NormalMap |
Texture | Normal map |
MetallicRoughnessMap |
Texture | R = metallic, G = roughness |
EmissiveMap |
Texture | Emissive glow texture |
Materials are typically created by the import pipeline or the editor. For programmatic creation:
let mat = Materials.CreatePBR("RedPlastic", "forward");
mat.SetColor("BaseColor", .(0.8f, 0.1f, 0.1f, 1));
mat.SetFloat("Roughness", 0.4f);
mat.SetFloat("Metallic", 0.0f);
let matRes = new MaterialResource(mat, true);
ResourceSystem.AddResource<MaterialResource>(matRes);
mat.SetBlendMode(.Opaque); // Default, no transparency
mat.SetBlendMode(.Masked); // Alpha test with AlphaCutoff
mat.SetBlendMode(.AlphaBlend); // Transparent blending
mat.SetBlendMode(.Additive); // Additive blending (particles, glow)
Scene-level rendering parameters are stored on RenderSceneModule, automatically injected into every scene.
if (let rs = scene.GetModule<RenderSceneModule>())
{
// Sky
var skyRef = ResourceRef(.Empty, "builtin://skies/realistic_sky.texture");
defer skyRef.Dispose();
rs.SetSkyTextureRef(skyRef);
rs.SkyIntensity = 1.2f;
// Ambient
rs.AmbientColor = .(0.1f, 0.1f, 0.15f);
// Post-processing
rs.Exposure = 1.0f;
}
These values are serialized with the scene and editable in the editor inspector when no entity is selected.
Shadows are automatic for lights with CastsShadows = true. The renderer supports:
Shadow quality is controlled per-light:
light.CastsShadows = true;
light.ShadowBias = 0.002f;
light.ShadowNormalBias = 0.5f;
Textured billboard quads for in-world effects, icons, or 2D elements.
let spriteMgr = scene.GetModule<SpriteComponentManager>();
if (let sprite = spriteMgr.Get(spriteMgr.CreateComponent(entity)))
{
var texRef = ResourceRef(.Empty, "project://textures/icon.texture");
defer texRef.Dispose();
sprite.SetTextureRef(texRef);
sprite.Size = .(1, 1);
sprite.Orientation = .CameraFacing; // Also: .CameraFacingY, .WorldAligned
}
Projected textures onto surfaces using depth reconstruction.
let decalMgr = scene.GetModule<DecalComponentManager>();
if (let decal = decalMgr.Get(decalMgr.CreateComponent(entity)))
{
var texRef = ResourceRef(.Empty, "project://textures/scorch.texture");
defer texRef.Dispose();
decal.SetTextureRef(texRef);
decal.Size = .(2, 2, 0.5f); // Width, height, depth of projection box
}
The renderer includes a post-processing stack with the following effects (in order):
| Effect | Description |
|---|---|
| SSAO | Screen-space ambient occlusion. Darkens crevices and contact areas. |
| Bloom | Bright areas glow. Downsample/upsample chain composited in HDR before tonemapping. |
| TAA | Temporal anti-aliasing. Jittered sampling with motion-vector reprojection. |
| Tonemap | ACES filmic tone mapping. Composites AO and bloom. Configurable exposure. |
| FXAA | Fast approximate anti-aliasing on the final LDR image. |
Post-processing is configured programmatically on the pipeline’s PostProcessStack. RenderSceneModule.Exposure controls the tonemap exposure.
DebugDraw provides immediate-mode 3D and 2D debug visualization:
let dbg = renderSub.DebugDraw;
// 3D (depth-tested)
dbg.DrawLine(.(0, 0, 0), .(5, 5, 0), .(1, 0, 0, 1));
dbg.DrawWireBox(bounds.Min, bounds.Max, .(0, 1, 0, 1));
dbg.DrawWireSphere(center, radius, .(0, 0, 1, 1));
// 2D screen overlay
dbg.DrawScreenText(10, 10, "FPS: 60", .(1, 1, 1));
dbg.DrawScreenRect(5, 5, 200, 30, .(0, 0, 0, 180));
Debug geometry is cleared each frame. Call draw methods during OnUpdate().
The rendering pipeline executes these passes in order: