-
Notifications
You must be signed in to change notification settings - Fork 5
Home
Welcome to the ShaderEditor wiki!
This wiki will help you understanding how this ShaderEditor software works.
The main goal of this software is to provide a tool to create advanced real time 3D effects. You can very quickly prototype new rendering techniques and try your algorithm before including them in a real 3D engine. This software can also be used as a support to learn real time 3D rendering and shaders, as you will be able to focus only on the interesting part of the programming, and not pay attention to the tedious OpenGL calls.
This software is based on the concept of render pass. A render pass is basically a step in the rendering of a frame. In the software, a render pass is defined by:
- A vertex shader
- A fragment shader
- An input tree
- A list of outputs
- An OpenGL state
The only time you will have to code will be to implement your vertex and fragment shader. Otherwise, everything is handled by a user interface that will be explained later.
This render pass system makes it very easy to implement complex graphical effects. For example, a render pass can draw the geometry in some buffers that can later be used to compute different post processes. This principle is called deferred rendering, in opposition with the forward rendering which draws the geometry at the same time as it computes the different effects.
When you first open the ShaderEditor software, you will see three panels. On the left side of the window, you will find the render pass manager panel which allows you to create new render passes and select the render pass you want to work with. In the middle, there is the render pass editor. This will allow you to edit the render pass that is currently selected in the render pass manager. On the right, you will find the data structure manager, which will allow you to create new data structures that you can then give as an input or output of a render pass.
Here is a list of the different data structures and their uses:
- Data Container: just an empty entity that can contain any data structures. It is useful to organize the projects and instantiate quickly a set of data.
- Vec2 Vertex Buffer: this is an array of 2D points. It can be useful to set some texture coordinates to a 3D mesh or for 2D rendering.
- Vec3 Vertex Buffer: this is an array of 3D points. It can be used to contain the 3D points of a mesh, or for example its normals or colors...
- Index Buffer: this is used in combination with a vec2 or vec3 vertex buffer to describe in what order the points will be drawn.
- Render Texture: this data structure can be used as an input for a shader as well as for an output. You can for example draw in a render texture in a render pass and then use what you have drawn as a texture in another render pass.
- Loaded Texture: this represents a texture loaded from an image file on your disk.
- Float: this is a floating point component that can be given as an input to a render pass.
- Vec2, Vec3, Vec4: these are vectorial components that can be given as an input to a render pass.
- Mat4: this is a matrix that can be given as an input to a render pass. It is usually used to apply 3D transformations.
To create a new data, just select which data you want to create and click on the "Create Data" button. The data will then appear in the list, and you can now select it and its UI will appear on the bottom of the list. You can see that each data has a "Save Changes" button. If you change the UI but you do not click on the "Save Changes" button, your modifications will not be taken into account. Remember to click on this button every time you are updating a data. You can also delete data by clicking on the "Delete Data" button. If you remove a container, all of its sons will also be removed.
When you select a container, you will see two buttons appearing: "Add Son" and "Remove Son". If you click on "Add Son" and then on another data, this data will be moved as a son of the selected container. If you click on "Remove Son" and then click on a data that is contained in the currently selected container, this data will be moved outside the container.
On the vertex buffer and index buffer data, you will find a combo box that will allow you to choose a preset from a list. It makes it much easier to instantiate a geometric shape for example, than typing each number one by one. Some of these presets go together. For example, if you create an indexed vertices shape, you will have to create the corresponding index buffer to be able to draw this data. In the future, the user will be able to load these buffers from 3D files.
Here you can see that an Index Buffer of type "IcoSphere index (2 recursions)" matches a Vertex Buffer of type "IcoSphere indexed Vertices (2 recursions)".
The render Textures UI is used to configure all the aspects of the created texture. The pixel format determines what the format of each pixel of the texture is. For example, GL_RGBA8 means 8 bits per component with four components. You can also choose to create a depth texture or a depth stencil texture. Then you can choose the size of your texture, its magnification and minification filters and the way it is wrapped when the texture coordinates are bigger than 1 and/or smaller than 0.
When selecting a loaded texture, you will see a thumbnail and a button "Load Texture" as well as some parameters which are similar to the render texture ones. You can click on the "Load Texture" button and choose a texture on your disk to be loaded. It can then be used as an input to a render pass.
These data structures are very basic and allow you to set values as inputs to the render pass. You will see editable spin boxes that you can edit when selecting these data structures.
This is also a basic data structure that can be passed as input. You can choose what kind of matrix you want and configure it to be either a view, projection or simple transformation matrix.
To create a render pass, click on the button "Create Render Pass" on the render pass manager panel. You will see your new render pass in the list. Select it and the render pass editor panel will then be activated.
The render pass editor is divided in four panels
- The input panel
- The output panel
- The code and configuration panel
- The visualizer
The input tree is used to pass data structures to the render pass. The tree is traversed from the root to the leafs, and each time a vertex buffer is found, it is rendered.
To set an input, you simply have to select the data you want as an input in the data structure manager and click on the "Instantiate" button. It will then appear in the input tree. If you instantiate a data container, all of its sons will be instantiated. You have to name the inputs so that you can use them in the shader. The default input names are "uniformData" followed by the unique ID of the instance.
There are seven simple rules to create the tree:
- All the uniforms needs to be filled for all the draw calls
- A uniform can be set only once per level in the tree
- If a uniform is set in two different levels of the tree, the deeper one overwrites the other and they must have the same type
- All the attributes must be filled for each draw call in the tree (therefore, if an attribute is present at one depth, all the other relevant attributes must also be present)
- All the vertex buffers must have the same number of vertices for the same draw call
- In case of indexed rendering, the index buffer must not contain any index higher than the number of vertices in the other vertex buffers
- There can only be one index buffer per draw call
A draw call is a set of vertex buffers (that can be coupled with an index buffer) instantiated at the same level in the tree. An attribute is an instance of a vertex buffer.
Here are some simple example to understand how this works:
-
Input Tree
- Mat4 'view' the matrix containing the camera position
- Mat4 'projection' the matrix containing the camera projection
-
Container this is going to contain the information to draw a cube
- Vec3 Vertex Buffer 'position' the vertices of the cube
- Vec2 Vertex Buffer 'uv' the texture coordinates of the cube
- Loaded Texture 'diffuse' the texture of the cube
- Mat4 'model' the model matrix of the cube
-
Container this is going to contain the information to draw a plane
- Vec3 Vertex Buffer 'position' the vertices of the plane
- Vec2 Vertex Buffer 'uv' the texture coordinates of the plane
- Loaded Texture 'diffuse' the texture of the plane
- Mat4 'model' the model matrix of the plane
This is a valid input tree: it complies with all the rules defined above.
-
Input Tree
- Mat4 'view' the matrix containing the camera position
- Mat4 'projection' the matrix containing the camera projection
-
Container this is going to contain the information to draw a cube
- Vec3 Vertex Buffer 'position' the vertices of the cube
- Vec2 Vertex Buffer 'uv' the texture coordinates of the cube
- Loaded Texture 'diffuse' the texture of the cube
- Mat4 'model' the model matrix of the cube
-
Container this is going to contain the information to draw a plane
- Vec3 Vertex Buffer 'position' the vertices of the plane
- Loaded Texture 'diffuse' the texture of the plane
- Mat4 'model' the model matrix of the plane
This is not a valid input tree, the second draw call does not have a 'uv' vertex buffer, the rule n°4 is not fulfilled. This will show an error message in the software.
The example explained above with the corresponding rendering.
To be able to visualize your rendering, you will need to draw your render pass in a render texture. This is very easy, you just have to select the render texture you want to draw in the data structures' manager and click on the output to which you want to bind it. If you want to be able to activate the depth test, you will also have to set a depth texture as an output.
The selected output in the table is the texture that is going to be displayed by the render pass visualizer.
You can now finally code your shader. You have two tabs that are made to write your code, one for the vertex shader and the other one for the fragment shader. It uses the version 150 of GLSL.
You can get all the inputs you have defined in your input tree in the code and write in the output textures.
To write in the output textures, you simply have to store the data (vec4) you want to write in ColorN, where N is the identifier of the color output you want to write in.
It is also possible to use some snippets of code. You just have to choose a function to create in the combo box under the text editor and click on "Add Snippet". This will add the definition of the selected function above the code your are currently working on.
Random function snippet
The last tab next to the code editor is the OpenGL state editor. This will allow you to choose the configuration of your render pass, for example choosing to activate or not the depth test or the face culling, or change the blend mode.
Here is an example of OpenGL configuration. The blend mode is set to "Additive" so that multiple lights can be drawn in the same render pass.
By pressing on the "Reload Visual" button, the visualizer will render all the render passes until it reaches the one you are working on, and it will display the selected output on the panel. It means you can at all moment display any output of any render pass just by selecting the render pass and the output you want by clicking on this button.
You can easily save and load some projects by clicking in the "project" button on the menu bar and then "save" or "open".
Some samples are given with the released version, you can try to load them, select the last render pass and click on "Reload Visual" to see the final result.