RenderSystem

The RenderSystem is a core component of Raftel Engine that manages the rendering pipeline. It provides two primary rendering techniques: Forward Rendering and Deferred Rendering, with support for advanced features like shadows, High Dynamic Range (HDR), tonemapping, and skybox rendering.

Key Features

  • Flexible rendering pipeline with support for both Forward and Deferred Rendering
  • Integrated shadow mapping system
  • High Dynamic Range (HDR) rendering with configurable tonemapping
  • Automatic or manual tonemapping adjustment
  • Support for skybox rendering
  • Custom shader support
  • Entity Component System integration

Rendering Pipelines

Raftel Engine offers two distinct rendering pipelines that can be used depending on your application's requirements:

Forward Rendering

A traditional rendering approach where each object is rendered in a single pass with all lighting calculations performed for each fragment. This approach is simpler and works well for scenes with fewer lights.

Deferred Rendering

A multi-pass technique that first renders scene geometry information to G-buffers, then performs lighting calculations in a separate pass. This approach is more efficient for scenes with many lights.

Using Forward Rendering

Forward rendering is the simplest approach and is ideal for scenes with a moderate number of lights. To use forward rendering, simply call the UpdateForwardRenderSystem method:

Basic Forward Rendering
// Initialize the engine and create required objects
Raftel::EntityManager entityManager;
Raftel::Camera camera;

// In your render loop
// Render the scene using forward rendering (no shadows)
Raftel::RenderSystem::UpdateForwardRenderSystem(entityManager, camera, false);

// To enable shadows
Raftel::RenderSystem::UpdateForwardRenderSystem(entityManager, camera, true);

When to Use Forward Rendering

Forward rendering is a good choice when:

  • Your scene has a small to moderate number of lights
  • You need to support transparency easily
  • You're targeting lower-end hardware
  • You need simpler implementation and debugging

Forward Rendering with Shadows

The UpdateForwardRenderSystem method accepts a boolean parameter to toggle shadow rendering:

Forward Rendering Method Signature
/**
 * @brief Updates and renders the entire scene using forward rendering.
 * @param em The entity manager that manages entities in the scene.
 * @param cam The camera used to view the scene.
 * @param shadow A boolean flag to indicate whether shadows should be rendered. Default is false.
 */
static void UpdateForwardRenderSystem(EntityManager& em, Camera& cam, bool shadow = false);

When shadow is set to true, the RenderSystem will perform additional passes to generate shadow maps and apply them to the scene.

Using Deferred Rendering

Deferred rendering separates the geometry and lighting passes, making it more efficient for scenes with many lights. To use deferred rendering, call the UpdateDeferredRenderSystem method:

Basic Deferred Rendering
// Initialize the engine and create required objects
Raftel::EntityManager entityManager;
Raftel::Camera camera;

// In your render loop
// Render the scene using deferred rendering (with shadows)
Raftel::RenderSystem::UpdateDeferredRenderSystem(entityManager, camera, true);

// To disable shadows
Raftel::RenderSystem::UpdateDeferredRenderSystem(entityManager, camera, false);

When to Use Deferred Rendering

Deferred rendering is beneficial when:

  • Your scene has many lights (tens or hundreds)
  • You want to implement advanced lighting effects efficiently
  • You're targeting mid to high-end hardware
  • You don't need to handle transparency in the same pass

Deferred Rendering with Shadows

Similar to forward rendering, the UpdateDeferredRenderSystem method accepts a boolean parameter to toggle shadow rendering:

Deferred Rendering Method Signature
/**
 * @brief Updates the deferred rendering system.
 * @param em The entity manager that manages the entities to be rendered.
 * @param cam The camera used to view the scene.
 * @param bShadows A boolean flag to indicate whether shadows should be rendered. Default is true.
 */
static void UpdateDeferredRenderSystem(EntityManager& em, Camera& cam, bool bShadows = true);

Note that unlike the forward rendering method, shadows are enabled by default in deferred rendering.

High Dynamic Range (HDR) Rendering

Raftel Engine supports High Dynamic Range (HDR) rendering, which allows for a wider range of color values and more realistic lighting. HDR rendering can be enabled or disabled through the Engine singleton:

Enabling HDR Rendering
// Enable HDR rendering
Raftel::Engine::Instance().HDR = true;

// Disable HDR rendering
Raftel::Engine::Instance().HDR = false;

What is HDR?

High Dynamic Range (HDR) rendering allows the engine to work with a wider range of luminance values than traditional 8-bit per channel rendering. This enables more realistic lighting with brighter highlights, deeper shadows, and more detailed color gradients. HDR is particularly effective for scenes with bright light sources, reflective surfaces, or dramatic lighting conditions.

Tonemapping

When using HDR rendering, tonemapping is required to convert the high dynamic range values to the limited range that can be displayed on standard monitors. Raftel Engine provides several options for tonemapping:

Configuring Tonemapping
// Enable tonemapping (required for HDR)
Raftel::Engine::Instance().TonneMapping = true;

// Choose between automatic or manual tonemapping
Raftel::Engine::Instance().AutoTonemapping = true;  // Automatic adjustment based on scene luminance

// Manual tonemapping parameters (when AutoTonemapping is false)
Raftel::Engine::Instance().exposure = 1.0f;         // Controls overall brightness
Raftel::Engine::Instance().contrast = 1.1f;         // Controls contrast between light and dark areas
Raftel::Engine::Instance().saturation = 1.05f;      // Controls color intensity
Raftel::Engine::Instance().targetMiddleGray = 0.18f; // Reference value for middle gray
Raftel::Engine::Instance().adaptationSpeed = 0.05f;  // Speed of adaptation to brightness changes

Important Note About HDR

When HDR is enabled, make sure to also enable tonemapping (TonneMapping = true). Without tonemapping, HDR values will be incorrectly clamped to the display range, leading to visual artifacts or washed-out colors.

Skybox Rendering

The RenderSystem also supports skybox rendering for creating realistic outdoor environments or background scenes. To set up a skybox, use the SetSkyboxTexture method:

Setting Up a Skybox
// Load a cubemap texture
std::shared_ptr<Raftel::Texture> cubemapTexture = /* load your cubemap texture */;

// Set the skybox texture
Raftel::RenderSystem::SetSkyboxTexture(cubemapTexture);

Once a skybox texture is set, the RenderSystem will automatically render it as the background of your scene.

Custom Shaders (Forward Rendering Only)

Raftel Engine allows you to customize the rendering process by replacing the core shader with your own shaders when using Forward Rendering. This gives you full control over the visual appearance of your scene:

Using Custom Shaders with Forward Rendering
// Change shaders using file paths
Raftel::RenderSystem::ChangeCoreShader("shaders/custom_vertex.vert", "shaders/custom_fragment.frag");

// Include a geometry shader
Raftel::RenderSystem::ChangeCoreShader("shaders/custom_vertex.vert", "shaders/custom_fragment.frag", "shaders/custom_geometry.geom");

// Restore the default shader
Raftel::RenderSystem::SetDefaultCoreShader();

Important: Forward Rendering Only

Custom shaders can only be used with the Forward Rendering pipeline. The Deferred Rendering pipeline uses a specialized internal shader system that cannot be directly replaced with custom shaders. If you need to customize the visual appearance when using Deferred Rendering, consider using post-processing effects instead.

Custom Shader Requirements

When creating custom shaders for use with the Forward Rendering pipeline, make sure to maintain compatibility with the engine's uniform naming conventions and input/output formats. See the Shader documentation for more details on required uniforms and attributes.

Ambient Lighting

Ambient lighting can be configured through the Engine singleton to provide basic illumination for all objects in the scene:

Configuring Ambient Light
// Enable ambient light
Raftel::Engine::Instance().has_ambient_light = true;  // Use Raftel::TRUE

// Set ambient light color (RGB values from 0.0 to 1.0)
Raftel::Engine::Instance().ambient_light = glm::vec3(0.1f, 0.1f, 0.1f);

Debugging Features

Raftel Engine includes several debugging features to help visualize and troubleshoot rendering issues. These features can be accessed through the engine's editor interface:

Editor Mode

The engine's editor mode provides access to additional debugging features, including:

  • Rendering entity bounding boxes
  • Real-time adjustment of rendering parameters
  • Visualization of lighting and shadow maps

To learn how to activate and use the editor, refer to the Entity Component System Tutorial.

Implementation Flow

Understanding the internal flow of the rendering pipeline can help you optimize your application and better integrate custom rendering solutions:

Forward Rendering Pipeline Flow

  1. Update Core Systems: Prepares basic rendering systems and transforms
  2. Update Transform System: Updates entity transformations
  3. Update Camera System: Updates camera matrices and view parameters
  4. Shadow Map Generation (if shadows enabled): Renders the scene from light's perspective
  5. Update Light System: Configures lighting parameters
  6. Render Scene: Renders all entities with lighting and materials
  7. Render Skybox (if configured): Renders the background skybox
  8. Post-processing: Applies HDR and tonemapping if enabled

Deferred Rendering Pipeline Flow

  1. Update Core Systems: Prepares basic rendering systems and transforms
  2. Update Transform System: Updates entity transformations
  3. Update Camera System: Updates camera matrices and view parameters
  4. Geometry Pass: Renders scene information to G-buffers (position, normals, albedo, etc.)
  5. Shadow Map Generation (if shadows enabled): Renders the scene from light's perspective
  6. Lighting Pass: Performs lighting calculations using G-buffer data
  7. Render Skybox (if configured): Renders the background skybox
  8. Post-processing: Applies HDR and tonemapping if enabled

Performance Considerations

Choosing the Right Pipeline

The choice between forward and deferred rendering depends on your scene complexity and hardware targets:

  • Forward Rendering is generally faster for scenes with few lights and works better with transparency
  • Deferred Rendering scales better with many lights but requires more memory for G-buffers
  • Shadows significantly impact performance, especially with many shadow-casting lights
  • HDR and Tonemapping add some overhead but greatly improve visual quality
Optimizing Rendering Performance
// For simpler scenes or lower-end hardware
// Use forward rendering without shadows
Raftel::RenderSystem::UpdateForwardRenderSystem(entityManager, camera, false);
Raftel::Engine::Instance().HDR = false;

// For complex scenes with many lights
// Use deferred rendering with shadows
Raftel::RenderSystem::UpdateDeferredRenderSystem(entityManager, camera, true);
Raftel::Engine::Instance().HDR = true;
Raftel::Engine::Instance().TonneMapping = true;

Complete Example

Here's a complete example that demonstrates how to set up and use the RenderSystem in a typical application:

Complete RenderSystem Usage Example
#include "raftel/systems.hpp"
#include "raftel/window.hpp"
#include "raftel/camera.hpp"
#include "raftel/entity_manager.hpp"
#include "raftel/texture.hpp"

int main() {
    // Initialize engine systems
    auto windowSystem = Raftel::WindowSystem::make();
    auto window = Raftel::Window::make("Raftel Engine Demo", *windowSystem);
    
    // Initialize render system
    Raftel::RenderSystem::Initialize();
    
    // Configure rendering settings
    Raftel::Engine::Instance().HDR = true;
    Raftel::Engine::Instance().TonneMapping = true;
    Raftel::Engine::Instance().AutoTonemapping = true;
    
    // Configure ambient light
    Raftel::Engine::Instance().has_ambient_light = true;
    Raftel::Engine::Instance().ambient_light = glm::vec3(0.1f, 0.1f, 0.15f);
    
    // Create camera and entity manager
    Raftel::Camera camera;
    Raftel::EntityManager entityManager;
    
    // Set up skybox (optional)
    auto skyboxTexture = /* load your cubemap texture */;
    Raftel::RenderSystem::SetSkyboxTexture(skyboxTexture);
    
    // Create entities, lights, meshes, etc.
    // ...
    
    // Main render loop
    while (!window->ShouldClose()) {
        // Update camera, input, physics, etc.
        // ...
        
        // Choose your rendering approach:
        
        // Option 1: Forward rendering with shadows
        Raftel::RenderSystem::UpdateForwardRenderSystem(entityManager, camera, true);
        
        // Option 2: Deferred rendering with shadows (alternative)
        // Raftel::RenderSystem::UpdateDeferredRenderSystem(entityManager, camera, true);
        
        // Swap buffers and handle events
        window->swapBuffers();
    }
    
    // Clean up
    Raftel::RenderSystem::Shutdown();
    
    return 0;
}

Related Topics

Shaders

Learn about the shader system that integrates with the RenderSystem.

Lighting

Explore how to set up and configure lights in your scene.

Entity Component System

Learn how to use the editor and debug rendering features.