ShaderGen is a node-based shader editor that is still in development. It can be used as an artist-friendly material editor inside other applications (for example, a graphincs engine). The application can control which nodes to expose to the artist, as well as define new application-specific nodes.
I was trying to create a video demonstrating the system, but the audio quality was very bad. But here it is after speeding it up anyway.
If the video is not working, you can download it directly here.
The demo is built using Direct3D 9 and HLSL (uses the D3DXEffect framework), but the system is not tied to a specific API. ShaderGen uses the graph of nodes to generate code and then feed that code to the shader compiler. Making changes to the nodes changes the generated code and thus requires recompiling the shader.
ShaderGen does not generate complete shaders, nor does it aim to. Typically an engine/game will create a shader system that is responsible for calculating lighting, shader permutations... etc. ShaderGen is then used to generate code that the application will plug into its existing shader code. This empowers artists while still giving the application developer complete control over what can be customized and how.
A node type can have optional input parameters. The code for that node then needs to check if that parameter is missing or not. That check is a pre-processor conditional (#if) in order to avoid "missing variable" compile errors. For example, consider the adjacent screenshot. The code in the screenshot is the code generated for the UVs node.
The "code" property of the UVs node type is as follows:
uv = IN.tex0; #if( !XRepeat_missing) uv.x *= XRepeat; #endif #if( !YRepeat_missing) uv.y *= YRepeat; #endif #if( !XPanSpeed_missing) uv.x += g_fTime * XPanSpeed; #endif #if( !YPanSpeed_missing) uv.y += g_fTime * YPanSpeed; #endif
Notice that the run-time will convert
<variable_name>_missing to either 0 or 1 depending on whether the input variable is connected or not. This is why XRepeat_missing was converted to 0 while the rest were converted to 1.
Also notice that XRepeat was replaced with 4.0000, which is the value feeding into the XRepeat parameter. Had this parameter been connected to the result of some calculation (instead of directly to an output parameter designated as "fixed"), the 4.0000 would be replaced with the name of a variable generated to hold the result of that calculation.
The screenshot shows the Node Type editor window displaying the properties of the UVs node. Notice the "overloads" section. To generate code for a specific node (so that code for nodes which use its outputs works), the types of its outputs needs to be determined. But the type of an output parameter can depend on the types of the inptus of the node. For example, consider the Add node. Adding two vectors will give us a vector. We certainly don't want to create many types of Add nodes (AddVectors, AddScalars, AddVectorScalar... etc).
This is why overloads are used. An overload is a combination of types for the inputs and outputs of a window type. Type properties of each parameter can be specified directly, or they can be set to "same as x", where x is any other parameter of the node. Notice the dropped-down list in the screenshot. However, output parameters can't have "any" in their type properties. Also, if an output parameter has "same as some_input" in its type properties, the shader can not compile until some_input is connected ands its type is determiend.
Some Demo Details
The demo uses the following libraries:
- Win32 Property Grid Control: a light-weight but versatile pure win32 native control.
- Code-section's Toolbox (CSTB): Specifically, intrusive trees and lists.
- GraphEd: A node graph editor control still under development.
- TinyXML2 for storing node type information.
- Direct3D 9 / HLSL / D3DXEffect
If you have any questions of feedback, please comment below or contact me by email. Thanks for looking!