Shaders
Shaders can be used in materials by defining a program property.
The program property can define different shaders that will be used as part of the combined linker step.
Here is a simple shader example (defined as BasicShader in tests):
{ "pass": [ { "lighting": false, "cullMode": "none", "textureUnit": [ { "image": "data/EchoLogo.png" }, { "image": "data/EchoLogoInternalGlow.png" } ], "program": { "vertexShader": [ "Basic" ], "fragmentShader": [ "Basic" ], "matrixVariable": "matViewProjection" } } ] }
Resource setup
In the above material example can see there are two shaders that make up the material's shader program. These values are the names of the shaders as defined in each of the ShaderManager instances for the corresponding shader type. If you are using an Application to manage your resource managers and load via an application configuration file you can define your shader list files using the following options:
fragment.shader.list=data/shaders.list vertex.shader.list=data/shaders.list
Since shaders are defined by a different option name in this example we are using the same list file to define all types of shaders. You can separate this in whatever logical way makes sense for your use case.
The shaders.list file defines the shaders which are both named Basic as referenced by the material. The names don't conflict here because the resources are defining two different resource types:
fragment.shader=data/BasicFragment.glsl,Basic vertex.shader=data/BasicVertex.glsl,Basic
These glsl files are text files that contain the shader code.
Shader variables
Shader variables can be bound in your material file which makes them available via the ShaderProgram in the RenderPass.
There are a number of predefined shader variables that Echo will set for you if you define the variable name in the material. These are:
- matrixVariable - Combined view projection world matrix
- cameraPositionVariable - Position of the current Camera
- worldMatrix - The current entity's world matrix
- viewMatrix - The current view matrix derived from the Camera
- projectionMatrix - The current projection matrix derived from the Camera
Special variables
RenderPass attempts to look up a number of uniform variables:
uniform int numberOfLights; uniform vec3 lightPosition[YOUR_MAX_NUMBER_OF_SUPPORTED_LIGHTS]; uniform vec3 lightColour[YOUR_MAX_NUMBER_OF_SUPPORTED_LIGHTS]; uniform float lightPower[YOUR_MAX_NUMBER_OF_SUPPORTED_LIGHTS];
These all need to be defined in order for one or more lights to work. The arrays should all be the same size and the size defines the maximum number of lights your shader supports.
- numberOfLights - Is set to the number of lights define in the scene or the maximum defined in the shader
- lightPosition - Light positions
- lightColour - Light colours
- lightPower - Light power
Custom variables
A shader script without access to external data is a bit limited. You can define additional variables in the variable list. The above basic example does not do this but the PBR materials do:
{ "pass": [ { "lighting": false, "cullMode": "back", "textureUnit": [ { "image": "rustediron2_albedo.png" }, { "image": "rustediron2_normal-ogl.png" }, { "image": "rustediron2_metallic.png" }, { "image": "rustediron2_roughness.png" }, { "image": "rustediron2_ao.png" } ], "program": { "vertexShader": [ "PBR" ], "fragmentShader": [ "PBR" ], "matrixVariable": "matViewProjection", "cameraPositionVariable": "camPos", "worldMatrix": "model", "viewMatrix": "view", "projectionMatrix": "projection", "variable": [ "albedoMap Sampler(0)", "normalMap Sampler(1)", "metallicMap Sampler(2)", "roughnessMap Sampler(3)", "aoMap Sampler(4)" ] } } ] }
In this example each variable is defined by two string parameters:
variableName value
This variable list is designed to let you set static values for your script variables OR you can use the special Sampler value to bind texture samplers to the variables. There are 8 value types and one binder type.
Values:
- Vector4(x,y,z,w)
- Vector3(x,y,z)
- Vector2(x,y)
- Matrix4(00,01,02,03,10,11,12,13,20,21,22,23,30,31,32,33)
- Matrix3(00,01,02,10,11,12,20,21,22)
- int(n)
- bool(true|false|0|1)
- float(v)
Binders:
- Sampler(n) - The above example binds each of the texture units to the various PBR components.
Accessing variables from code
You can set script variable values from your application by accessing the shared object via the program.
// Remember to error check shared_ptr<T> myVariable = material->GetPass(0)->GetProgram()->GetUniformVariable<T>("variableName",createIfNotFound);
For example lets say we have a script with the following variable:
uniform vec3 dimensionalWarp; // Create cool axial colour effects based on a warp factor
Once built the variable can be accessed using:
// Remember to error check shared_ptr<Vector3> dimensionalWarp = material->GetPass(0)->GetProgram()->GetUniformVariable<Vector3>("dimensionalWarp", true);
Later you can set the value:
// Remember to null check *dimensionalWarp = Vector3(2,1,2); // Warping along the XY plane
You can alternatively use ShaderProgram::SetUniformVariable() however this will result in a look up each time you call it.
material->GetPass(0)->GetProgram()->SetUniformVariable("dimensionalWarp", Vector3(2,1,2), true); // Alternatively if you need to set via string a conversion is attempted first material->GetPass(0)->GetProgram()->SetUniformVariable("dimensionalWarp", "Vector3(2,1,2)", true);
There isn't anything fancy about the uniform variables. They are just shared pointers which are accessed when a ShaderProgram is compiled and linked by the platform's implementation. The implementation will access or create the variable bindings in the ShaderProgram and, during rendering, is responsible for setting the script variables from the shared pointers.
Attributes
If you're familiar with GLSL shaders you may know that there are other types of variables called attributes. These are things like positions normals, colours, texture coordinates for your vertex shaders. You can define custom attributes for the vertices in your meshes by adding custom vertex attributes to the SubMesh VertexBuffer. This can be done in your 3D modelling program or in code using VertexBuffer::AddVertexAttribute() to build the SubMesh vertex format. Keep in mind that you need to Allocate() after setting up the attribute format and before setting any data to ensure your vertex buffer has enough space for your vertices.
- Last Author
- 0xseantasker
- Last Edited
- Apr 2 2024, 11:18 PM