5.4 Using shaders


A shader is a small program that runs on the GPU to create rendering effects that would be difficult or impossible to implement using the fixed-function pipleine. Visualize supports custom shaders in the DirectX9, DirectX11, and OpenGL2 drivers. Shaders must be written in HLSL. Instructions for authoring HLSL is beyond the scope of this document, however, once the shader is written, it is simple to use in Visualize by following the steps below:

Step 1: Acquire or otherwise import the shader code

The shader source can be read from a file or passed to Visualize as a UTF-8 encoded string (C# users can simply use String).

[snippet 5.4.a]
static const char *shader_source =
"void custom_shader(const HGlobals globals, inout HColor color) { \n"
"color.diffuse.rgb = globals.tex.coords.rgb;}\n"
"#define H3D_COLOR_SHADER custom_shader\n";
String shader_source =
"void custom_shader(const HGlobals globals, inout HColor color) { \n" +
"color.diffuse.rgb = globals.tex.coords.rgb;}\n" +
"#define H3D_COLOR_SHADER custom_shader\n";

Step 2: Build the shader into a portfolio

In Visualize, shaders live in portfolios, and portfolios are referenced by segments. Thus, if the geometry is to use a shader, it must be in a segment which uses the portfolio (portfolios are inherited down the segment tree like other attributes). The only way to define a shader is by using a HPS::ShaderKit.

[snippet 5.4.b]
HPS::ShaderKit shaderKit;
// copies the HLSL into the kit
shaderKit.SetSource(shader_source);
// create (or alternatively use an existing) portfolio
// imports the defined shader into the portfolio
portfolioKey.DefineShader("myShader", shaderKit);
// instructs the segment to use the portfolio
mySegmentKey.GetPortfolioControl().Push(portfolioKey);
HPS.ShaderKit shaderKit = new HPS.ShaderKit();
// copies the HLSL into the kit
shaderKit.SetSource(shader_source);
// create (or alternatively use an existing) portfolio
HPS.PortfolioKey portfolioKey = HPS.Database.CreatePortfolio();
// imports the defined shader into the portfolio
portfolioKey.DefineShader("myShader", shaderKit);
// instructs the segment to use the portfolio
mySegmentKey.GetPortfolioControl().Push(portfolioKey);

Step 3: Set the geometry to use the shader

The last step is to set the segment's geometry to use the shader for the face material. In the example below, a shell is used to demonstrate an effect that would be cumbersome to produce using other methods. While this example is trivial, shaders can be extended to produce complex effects.

[snippet 5.4.c]
mySegmentKey.GetMaterialMappingControl().SetFaceShader("myShader");
mySegmentKey.GetMaterialMappingControl().SetFaceShader("myShader");

[figure 5.4.a] A shader is an easy way to interpolate color data across multiple shell faces