Urho3D
|
Much of the rendering functionality in Urho3D is built on two subsystems, Graphics and Renderer.
Graphics implements the low-level functionality:
Screen resolution, fullscreen/windowed, vertical sync and hardware multisampling level are all set at once by calling Graphics's SetMode() function. There is also an experimental option of rendering to an existing window by passing its OS-specific handle to SetExternalWindow() before setting the initial screen mode.
When setting the initial screen mode, Graphics does a few checks:
Renderer implements the actual rendering of 3D views each frame, and controls global settings such as texture quality, material quality, specular lighting and shadow map base resolution.
To render, it needs a Scene with an Octree component, and a Camera that does not necessarily have to belong to the scene. The octree stores all visible components (derived from Drawable) to allow querying for them in an accelerated manner. The needed information is collected in a Viewport object, which can be assigned with Renderer's SetViewport() function.
By default there is one viewport, but the amount can be increased with the function SetNumViewports(). The viewport(s) should cover the entire screen or otherwise hall-of-mirrors artifacts may occur. By specifying a zero screen rectangle the whole window will be used automatically. The viewports will be rendered in ascending order, so if you want for example to have a small overlay window on top of the main viewport, use viewport index 0 for the main view, and 1 for the overlay.
Viewports can also be defined for rendertarget textures. See Auxiliary views for details.
Each viewport defines a command sequence for rendering the scene, the render path. By default there exist forward, light pre-pass and deferred render paths in the bin/CoreData/RenderPaths directory, see SetDefaultRenderPath() to set the default for new viewports. If not overridden from the command line, forward rendering is the default. Deferred rendering modes will be advantageous once there is a large number of per-pixel lights affecting each object, but their disadvantages are the lack of hardware multisampling and inability to choose the lighting model per material. In place of multisample antialiasing, a FXAA post-processing edge filter can be used, see the MultipleViewports sample application (bin/Data/Scripts/09_MultipleViewports.as) for an example of how to use.
The steps for rendering each viewport on each frame are roughly the following:
In the default render paths, the rendering operations proceed in the following order:
The rendering-related components defined by the Graphics and UI libraries are:
Additionally there are 2D drawable components defined by the Urho2D sublibrary.
The following techniques will be used to reduce the amount of CPU and GPU work when rendering. By default they are all on:
Note that many more optimization opportunities are possible at the content level, for example using geometry & material LOD, grouping many static objects into one object for less draw calls, minimizing the amount of subgeometries (submeshes) per object for less draw calls, using texture atlases to avoid render state changes, using compressed (and smaller) textures, and setting maximum draw distances for objects, lights and shadows.
In some applications, like stereoscopic VR rendering, one needs to render a slightly different view of the world to separate viewports. Normally this results in the view preparation process (described above) being repeated for each view, which can be costly for CPU performance.
To eliminate the duplicate view preparation cost, you can use SetCullCamera() to instruct a Viewport to use a different camera for culling than rendering. When multiple viewports share the same culling camera, the view preparation will be performed only once.
To work properly, the culling camera's frustum should cover all the views you are rendering using it, or else missing objects may be present. The culling camera should not be using the auto aspect ratio mode, to ensure you stay in full control of its view frustum.
On Direct3D9 and Android OpenGL ES 2.0 it is possible to lose the rendering context (and therefore GPU resources) due to the application window being minimized to the background. Also, to work around possible GPU driver bugs the desktop OpenGL context will be voluntarily destroyed and recreated when changing screen mode or toggling between fullscreen and windowed. Therefore, on all graphics APIs one must be prepared for losing GPU resources.
Textures that have been loaded from a file, as well as vertex & index buffers that have shadowing enabled will restore their contents automatically, the rest have to be restored manually. On Direct3D9 non-dynamic (managed) textures and buffers will never be lost, as the runtime automatically backs them up to system memory.
See IsDataLost() function in VertexBuffer, IndexBuffer, Texture2D, TextureCube and Texture3D classes for detecting data loss. Inbuilt classes such as Model, BillboardSet and Font already handle data loss for their internal GPU resources, so checking for it is only necessary for custom buffers and textures. Watch out especially for trying to render with an index buffer that has uninitialized data after a loss, as this can cause a crash inside the GPU driver due to referencing non-existent (garbage) vertices.
The only per-instance data that the rendering system supplies by itself are the objects' world transform matrices. If you want to define extra per-instance data in your custom Drawable subclasses, follow these steps:
See also Vertex buffers, Materials, Shaders, Lights and shadows, Render path, Skeletal animation, Particle systems, Zones, and Auxiliary views.
See Rendering modes for detailed discussion on the forward, light pre-pass and deferred rendering modes.
See Differences between rendering APIs for what to watch out for when using the low-level rendering functionality directly.