Shade: Semantic Types
Recall that when you work with Shade, you’re basically just dropping little nodes on a layout surface. Each node represents some kind of effect feature you’d like your finalized shader to exhibit, and each node has a number of input and output parameters you can connect to form a graph that defines relationships and dependancies between feature fragments.
Shade produces HLSL code when compiling a shader graph. Like most languages, HLSL’s type system is “simple” in the sense that it is only concerned with the fundamental type of a variable — this is perfectly acceptable for programming. Shade, however, requires more information to achieve its goals.
Take a look at the closeup of the example shader graph from the last article, below. The skinning node produces a “normal” output, containing the skinned normal. The diffuse lighting node expects a “normal” input so that it can perform the standard n-dot-l diffuse lighting calculation. Each shader feature is written in isolation, and generally has to make some assumptions about inputs. For example, this particular diffuse lighting feature assumes the input normal vector is in world space, because the shader global containing the light vector happens to be in world space. It would really clutter the UI to include all those assumptions on the display, and more importantly it would be exceedingly counterproductive for Shade to require the user to manually place little feature nodes to perform the appropriate conversions between coordinate spaces.

Fortunately, by adding additional information to the parameter types, we can have Shade inject those conversions automatically. This is the basis of Shade’s semantic type system. A semantic type is just a collection of name/value pairs called “traits” that describe contextual features of the type. The only trait that is required is that describing the fundamental type. Additional traits are optional, but more information is generally better (especially when finding conversions is concerned, as will be addressed in a later article). For example, the semantic type of the skinned normal is type:float3,use:normal,basis:model,range:unit. The input to the diffuse lighting feature is type:float3,use:normal,basis:view.
Imbuing the parameters with this extra information requires some additional effort on the part of the programmer crafting shader features, but the result is a more reliable interface for the artist building effects with those features since there is more information for the compiler to work with — it can perform more comprehensive sanity checks and automatic boilerplate. Without the extra semantic information, Shade could still generate HLSL that was correct and compiled. After all, the fundamental types of both parameters are the same — float3. But the extra information allows Shade to better understand the contexts in which the parameters exist, and makes it possible to ensure the values passed through those variables are put in the proper contexts before they are used.
The end result is a system that is both safer and easier to use, allowing more errors to be detected while at the same time keeping the graph uncluttered and not bogging the artist down with grunt work. Although the example shader I’m presenting here is quite simple and contains fairly low-level features, the ultimate goal of the tool is to have a system that involves fewer feature nodes (as a result, the feature nodes will be higher-level in nature). Part of that motivation is because this kind of visual pseudo-programming can become unmanageable quickly as the number of features increases; the semantic typing system allows for a great deal of clutter to be removed.
Next time, I’ll take a look at how Shade actually searches for and applies conversions between semantic types.
