I am writing my own rendering engine using the entity-component-system approach using EnTT. My idea is that I don't want to hard code my engine too much. I want to leave it open for the user to extend with new components and support writing their own shaders with framebuffer configurations. I do this more for myself to experiment with rendering techniques.
These are a selection of components I have. The example uses OpenGL terminology.
- Mesh component: Contains pure vertex data, such as position, normals, uv coordinates and tangent data. Render system uses it to create and fill vertex buffers and stores the id's (vbo, vao) in the Renderable object.
- Material: Contains properties of the material (i.e. uniforms, textures used, shader file names). Render system uses it to compile vertex shader/fragment shader (material contains path to the shaders). Program id is stored in the Renderable component. Material system updates the material properties to the program every frame. So MaterialSystem and RenderSystem are both OpenGL specific.
- Renderable: Component that contains all the state needed to perform glDraw* calls. I try to put the OpenGL state exclusive in the Renderable component.
It works fine up to now. The question that I have is:
1. How to deal with “higher-level components/materials or postprocessing effects”? These require multiple passes and different framebuffer configurations, chaining them together.
I am struggling with how to approach these higher order effects into the ECS analogy. Take for example Bloom, that can be represented as a multi-pass approach with multiple materials (i.e. brightness extraction, blurring, blend).
My idea is:
- Create a bloom component and bloom system.
- The bloom system creates a set of entities with material components reflecting the state of the bloom component.
- The bloom system updates every frame (or triggers only when parameters are changed) and reflects those changes to the parameters.
Like bloom, there will likely be more higher level components (shadow for example).
2. Is it a proper approach to write systems that add lower level components based on a higher order component? These systems actually act more as factories with the added responsibility to keep the parameters in sync.
Could anyone shine a light on whether my thinking is correct or whether it's considered bad practice?