NOTE: This is a work in progress.
At Echo's core there is a `Kernel` that updates `Tasks`. In order to get work done you create tasks which will be periodically updated. Rendering to the screen is one such task. Unlike other engines, which might have a strict update then render loop, Echo decentralises rendering. This allows you to decide when rendering occurs.
As far as Echo, or more specifically a Kernel` is concerned, rendering is just a job for another Task.
Rendering is performed by a `Renderer` which is a Task. When the `Renderer` tasks updates it renders a `Scene` through a `Camera` to a `RenderTarget` with the defined `Viewport` set.
You can set up multiple `Renderer` objects to perform rendering to multiple views on the same `RenderTarget`, this would allow you to perform split screen rendering. You can also set up multiple renderers might be to render the same scene to a different `RenderTarget` such as a texture or second display. Or the same scene through a different camera to a different render target.
If you are rendering two view ports to the same `RenderTarget` using two `Renderer` objects directly you will find that one will flicker. This is because a `Renderer` by itself is designed to perform pre and post rendering operations, such as clearing the screen and swapping the screen buffer. In order to render multiple viewports to the same target you will need to disable these pre and post operations on each `Renderer` depending on the order they should be rendered. To make this job simpler Echo provides a `MultiRenderer` class which you can use to create `Renderer` objects for the same target.
dot {{{
digraph A {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "box"
]
edge [
headlabel = ""
taillabel=""
arrowhead="odiamond"
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Renderer->RenderTarget
Renderer->Viewport
Renderer->Camera
Camera->Scene
MultiRenderer->Renderer
edge [
arrowhead = "empty"
headlabel = ""
taillabel=""
fontname = "Bitstream Vera Sans"
fontsize = 8
]
PlatformRenderTarget->RenderTarget
edge [
dir="back"
arrowhead = "empty"
arrowtail = "empty"
headlabel = ""
taillabel=""
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Task->Renderer
Task->MultiRenderer
}
}}}
When a `Camera` renders a `Scene` the Scene will build a render queue. The render queue of `SceneRenderables` which provide a position and bounds that can be used for sorting. The queue is sorted and then rendered in the appropriate order.
Most of the time you'll find that `SceneRenderable` objects are actually `SceneEntity` objects. `SceneEntity` objects are simple `SceneRenderable` objects that have a `Mesh`. The `Mesh` can be loaded from a file, a specialised `Mesh` object such as a `TextMesh`, or can be created manually. The `Mesh` is rendered by the `SceneEntity` when the `SceneEntity` is rendered in the `Scene`.
dot {{{
digraph A {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "box"
]
N [ label = "Node"]
edge [
headlabel = ""
taillabel=""
arrowhead="odiamond"
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Scene->SceneRenderable
SceneEntity->Mesh
edge [
arrowhead = "empty"
headlabel = ""
taillabel=""
fontname = "Bitstream Vera Sans"
fontsize = 8
]
SceneEntity->SceneRenderable
SceneRenderable->N
SceneRenderable->Renderable
Mesh->Renderable
}
}}}
There are a number of more useful specialised `SceneEntity` types as well as some specialised `Mesh` types that will construct `Mesh` objects as needed.
dot {{{
digraph A {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "box"
]
Element [ label="GUI::Element" ]
edge [
headlabel = ""
taillabel=""
arrowhead="odiamond"
fontname = "Bitstream Vera Sans"
fontsize = 8
]
edge [
arrowhead = "empty"
headlabel = ""
taillabel=""
fontname = "Bitstream Vera Sans"
fontsize = 8
]
TextEntity->SceneEntity
TextMesh->Mesh
Element->SceneEntity
Sprite->SceneEntity
}
}}}
Most of the time we are just rendering a `Mesh` object to a `RenderTarget`. The way that `Mesh` is displayed is determined by its `Material`.
Materials are made up of a number of passes. Each pass is essentially a render operation. A `RenderPass` defines the textures that will be rendered along with other properties such whether or not the object will be affected by lighting and how it should be blended with the existing scene.
NOTE: A `RenderPass` may also define a shader program that overrides how the object is displayed. Shader programs are really flexible but may not be supported on all platforms. At the very minimum Echo implements a consistent "simple" render pipeline through materials.
The textures are defined as TextureUnits which specify which image (the texture) will be used for rendering as well as how the texture will be blended with with other texture units. This topic can be quite complicated especially when we are using shaders. See [[/w/projects/echo_3/manual/graphics/materials/|this document]] for more information.
Materials can be defined in a file or through code. Defining materials in files means much less code is required. For more information on materials see [[/w/projects/echo_3/manual/graphics/materials/|this document]].
dot {{{
digraph A {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "box"
]
edge [
headlabel = "1"
taillabel="1"
arrowhead="odiamond"
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Mesh->Material
edge [
headlabel = "*"
taillabel="1"
arrowhead="diamond"
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Material->RenderPass
RenderPass->TextureUnit
edge [
arrowhead = "empty"
headlabel = ""
taillabel=""
fontname = "Bitstream Vera Sans"
fontsize = 8
]
}
}}}