Sunday, 23 November 2014

Shader Linker (Part 2)

Lately I had some time again to work in FlareTic.

I added some new nice pixel material functions, here are a couple of screenshots:

Now a lot of my procedural materials share a lot in common, they are mostly either wave/noise functions, which are then combined.

In the example above I use 4 noise functions (2 for roughness, 2 for reflectivity), which are then combined either as additive/multiply.

Now being able to combine those functions in an easier way would be rather handy. And hey, I already have a function linker patch to generate pixel shaders.

Even tho I have the base that is totally required (hybrid node/code linker), it still has some flaws.

First, you need a lot of swizzle operators. This adds a lot of pollution in the patch, to just process something like "set float to vector4".

But luckily, I already mentionned I had implicit converters.

So I created a simple version of them, which add extra instruction to linker, so it can call passvaluewithswizzle instead of passvalue.

If we look at the screenshot above, now instead of having a xxxx node to convert float to float4 we can see converter just added itself implicitely, reducing patch node pollution.

Now if we want to perform conversion the opposite way (for example, float4 to float), we run into an issue : which component to take?

But since I can add configuration to links (yes in FlareTic links can also have parameters that you can modify via inspector), this is suddenly trivial, I just add a swizzle parameter so we can change which component we want. 

In that case it had a little twist, since when this parameter changes, I need to ask the linker to build me a new pixelshader, but that's rather trivial.

You can see in screenshot above, selected link has a swizzle parameter (those links are yellow in the patch, since they imply a loss of data, and it makes it easier for the user to see they can modifiy link behaviour).

Next there's the most serious issue for usability. 
To be able to create a pixelshader, the graph must be complete, if an input pin does not have a connection link process will fail.

So I could easily provide a default, but let's think better, if an input pin has no connection, it would be much nicer to have the value in inspector and be able to change it real time.

So instead, I build a hidden RawBuffer, and a few reader functions.

When I parse the graph, if a pin is connected i call passvalue, if not, I ask the hiddenbuffer for a data slot, hiddenbuffer also returns me reader node, which will grab data from buffer.

Before to run the pixelshader, I grab the data from the inspector and copy to buffer, so it's easy to tweak input parameters if they are not connected.

You can see that the multiply node shows me value editor for non connected pin. Modifying value does not need to relink shader, it's just copied in buffer before the call.

Now once all of this is done, I just needed to create a Input/Output template for my deffered materials, and make sure I sort the calls properly. 

And here we go, hybrid code/patch material editor, promised I'll do some nicer screenshots next time ;)

Now for next feature set (still work in progress):
  • Function grouping
  • More aggressive packing for buffer data.
  • Custom cbuffer integration