Rendering
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.
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 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 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 ] } }}}
- Last Author
- 0xseantasker
- Last Edited
- Apr 3 2024, 11:26 PM