Shaded
Development
I implemented this project alone as my second semester project at the University of Applied Sciences Rapperswil during the spring semester in 2006.
Detailed Description
Shaded is a proof of concept for a new approach on shader programming. Instead of writing shaders using some sort of low or high level programming language the user can create his program visually. To accomplish this, Shaded focuses on the data that gets manipulated. The data flow is visualized by using a directed graph data structure. In this graph data structure the nodes represent some operation on one or more incoming data items. Each operation in turn outputs some new data items which can be further processed by other nodes. Finally one of these output elements is interpreted as a colour value and written to the frame buffer.
To support control flow, e.g. loops and conditional statements, Shaded supports a feature called frames. Frames basically consist of an internal subgraph representing the body of the statement and some special logic based on the control structure it represents. Frames can also be used to group code, in a sense just like functions in an imperative programming language.
To ease the development of shaders, Shaded also supports a life preview of the current shader. To be able to easily see all the effects the user can create a custom scene, place lights and apply the newly developed shaders to the models in the scene. Whenever he makes changes resulting again in a valid shader, the shader is compiled and applied to the scene.
Technical Details
- Programming Language: C#
- Development Environment: Visual Studio.NET 2005
- Project Size: 381 files, 45'657 code lines, 2'822 comment lines, 6'870 blank lines, 1.96 MiB code
- Project Duration: 14 Weeks
- Technology used: Managed DirectX 2.0, SlimDX
Feature List
- A user interface to visually create and modify program graphs
- A plugin infrastructure for different shader model representations
- Fully featuring shader model 3.0. including conditional statements
- Validation for the shader model 3.0 graph
- Many prefabricated graph elements like Lambert Diffuse Lighting, etc.
- A plugin infrastructure for different output formats
- A plugin to write HLSL code
- One or many previews using a freely configurable scene
- Some more features
Challenges
The main challenge of this project was that I faced for the first time such a huge amount of work to be finished in a given timeframe. Since this was a university project there was a clear deadline and missing the deadline would have meant bad grading. So I had to plan ahead and I had to be careful to only do the work that was planned in the timeframe.
In the end I underestimated the documentation effort. An additional problem was that I was used to do the work in teams. But since the amount of documentation necessary did not depend on the team size it meant that I had to do as much documentation work as a team of two or three people.
The last lesson I learned was, that drawing class diagrams of large systems is a difficult task because there is no space for all the details. So it is important to carefully choose which elements of all the classes presented are important and which are not important enough to be on the class diagram. One could also consider drawing multiple diagrams of the same class structure, each covering another aspect of the system.
Screenshots
Parallax Occlusion Mapping rendered by ATI RenderMonkey, using code generated by Shaded |
||
Code Sample
This code sample shows the main loop where the shader compiler converts the shader graph into a shader program.
public CompiledOutput GenerateCode()
{
// prepare
Reset();
PrepareOutputChannels();
GeneratePositionTransformation();
List<ShaderGraphElements.Element> todo =
new List<ShaderGraphElements.Element>(graph.Elements);
bool breaked = false;
while (todo.Count > 0)
{
for (int i = 0; i < todo.Count; ++i)
{
ShaderGraphElements.Element element = todo[i];
if (element.CanGenerateCode())
{
element.GenerateCode(this);
todo.Remove(element);
breaked = true;
break;
}
}
if (todo.Count > 0 && !breaked)
{
foreach (ShaderGraphElements.Element element in todo)
{
element.ReportProblems();
}
// since there's the breaked-flag when some element has generated new
// code we should never get to this place. if we get here, we can be sure
// that there's an infinite-loop-condition!
throw new Error.InfiniteLoopException(string.Format(
"Detected an Infinite-Loop-Condition in {0}! Aborting!", "shaderGraph"
));
}
breaked = false;
}
HandleInputChannels();
return CreateCompiledOutput();
}