Latest  | Search | Go
Edit this page   |   Attach file 

  Home | Tutorials | Technical Reference | Runtime | API Documentation | ContentCreation > ShaderWriting  

Writing Shaders in Reality

A Look At Some of Reality's Standard Shaders

shader_spec_nomask.jpg

  • Specularity with no control-map
    • (Diffuse * Normal-dot-LightTangent + Specular) * LightColor

shader_parallax.jpg

  • Parallax (virtual displacement) mapping with specularity
    • (OffsetCoordDiffuse * OffsetCoordNormal-dot-LightTangent + OffsetCoordSpecular) * LightColor

shader_spec_masked.jpg

  • Specularity with control-map
    • (Diffuse * Normal-dot-LightTangent + Specular*SpecularMask) * LightColor

shader_spec_masked_SH.jpg

  • Specularity with PRT and SH
    • (Diffuse + Specular*SpecularMask) * PRTLightingCoefficient

shader_translucent.jpg

  • Sorted HDR blending
    • Diffuse * PRTLightingCoefficient * Opacity + (1-Opacity)*BlendColorBuffer

shader_aniso.jpg

  • Anisotropic highlighting
    • (Diffuse + (1-(ViewTangent-dot-Normal)^2)) * Normal-dot-LightTangent * LightColor

shader_env.jpg

  • Control-mapped environment mapping with specularity
    • ((Diffuse + EnvMap*FresnelCalc*SpecularMask) * Normal-dot-LightTangent + Specular*SpecularMask) * LightColor

shader_mix.jpg

  • 4-blend mix mapping and detail mapping
    • (Diffuse*Mix.r + Diffuse2*Mix.g + Diffuse3*Mix.b + Diffuse4*Mix.a) * DetailMap * Normal-dot-LightTangent * LightColor

shader_anim.jpg

  • Shader animation (water scrolling normal map layers)
    • (((Diffuse + (EnvMap(EnvMapScrollSpeed*TimeValue) + EnvMap(-EnvMapScrollSpeed*TimeValue))*FresnelCalc*SpecularMask) * Normal-dot-LightTangent + Specular*SpecularMask) * LightColor) * (Opacity*(1-FresnelCalc)) + (1-(Opacity*(1-FresnelCalc)))*BlendColorBuffer

shader_hdr.jpg

  • HDR overexposure
    • Diffuse * OverBright

shader_selfillum.jpg

  • Control-mapped self illumination with HDR overexposure
    • (Diffuse * Normal-dot-LightTangent * LightColor)*(1-SelfIllumMap) + Diffuse*SelfIllumMap*OverBright

shader_refract.jpg

  • Per-pixel and per-vertex refraction based on RT color buffer distortion
    • (Diffuse * Normal-dot-LightTangent * LightColor)*Opacity + (1-Opacity)*BlendColorBuffer((Normal*ViewProjection).xy+BumpMap.rg)

shader_blur.jpg

  • Per-pixel blurring based on RT color buffer multisampling
    • (Diffuse * Normal-dot-LightTangent * LightColor)*Opacity + (1-Opacity)*8xSampleBlur(BlendColorBuffer)

Structure of a basic Vertex Shader & Pixel Shader

Reality's shaders currently support HLSL, and to add a new shader technique to Reality you simply create an FX file, put it in your Demo Game "shaders" subdirectory, and then apply it onto a mesh as described in ShaderUsage & RealityBuilder. You can then configure, in RealityBuilder or in the MaxShaderPlugin, all editable properties (including Textures) that are exposed with <> tags, such as:

float   SpecPower  <string Enable = "true"; > = 32;

You can, in fact, just use the "" tag on every var you want to expose, except for shader textures. That is, 2D Textures must be defined in this manner:

texture tNewDiffuse < string TextureType = "2D"; >;

While Cube Maps must be defined in this manner:

texture tEnvMap0 < string TextureType = "Cube"; >;

Now let's take a look at what a basic shader does. Here is an example of a simple Diffuse NL lit shader, in other words diffuse with "bump mapping".

// Always include this in your shader files in order to have the core Engine-set variables available for use
#include "Types.h"

//-----------------------------------------------------
// Our Basic Settings
//-----------------------------------------------------

// Defines a texture as exposed -- being settable -- within the 3dsmax shader tool and Reality Builder
// Our Diffuse texture map
texture tNewDiffuse <
    string TextureType = "2D";
>;

// Exposed U and V tiling values for the diffuse texture
float      NewDiffuseUTile<string Enable = "true"; > = 1;
float      NewDiffuseVTile<string Enable = "true"; > = 1;

// Sets up a texture sampler used by the pixel shader that contains the diffuse texture
sampler sNewDiffuse = sampler_state
{
   SRGBTexture   = (useSRGB);
   Texture       = (tNewDiffuse );
   AddressU      = Wrap;
   AddressV      = Wrap;
   MinFilter     = (texFilter);
   MagFilter     = (texFilter);
   MaxAnisotropy = (maxAnisotropy);
};

// Defines a texture as exposed -- being settable -- within the 3dsmax shader tool and Reality Builder
// Our Bump texture map -- note this is actually expecting an RGB/RGBA normal map, NOT a greyscale bump map 
// (the terminology is often interchangeable, but all Reality bump maps must actually be as normal maps before used in shaders).
texture tNewBump <
    string TextureType = "2D";
>;

// Exposed U and V tiling values for the normal map texture
float      NewBumpUTile<string Enable = "true"; > = 1;
float      NewBumpVTile<string Enable = "true"; > = 1;

// Sets up a texture sampler used by the pixel shader that contains the normal map texture
sampler sNewBump = sampler_state
{
   SRGBTexture   = (useSRGB);
   Texture       = (tNewBump );
   AddressU      = Wrap;
   AddressV      = Wrap;
   MinFilter     = (texFilter);
   MagFilter     = (texFilter);
   MaxAnisotropy = (maxAnisotropy);
};
//-----------------------------------------------------------------------------
// Our New Shader
//-----------------------------------------------------------------------------

/* This is a structure that the Vertex Shader fills out and passes to the Pixel Shader. 
Effectively, by storing float values into up 8 float4 TexCoord slots (on ps 2.0), you can pass data from Vertex Shader calculations to 
the Pixel Shader. For this basic Diffuse NL shader, we need to pass the Light Tangent (for L), 
the Light Distance (for attenuation), the Base (Diffuse) and Bump xy texture coordinates, 
and the view-space transformation that will allow us to blend over previous pixels in the framebuffer in secondary passes (more on this below). 

Thus, you can see the NewShader_Point_Pixel strcuture contains 4 TexCoords to store those data. 
The Position and Fog members are required to fill out in the Vertex Shader, as they determine the basic transformation of the vertex 
(this can be altered in a Vertex Shader supporting character animation), 
and its D3D fog value (which we don't use, instead fog can more effectively be calculated in the pixel shader).*/

struct NewShader_Point_Pixel
{
    float3 LightTangent : TEXCOORD0;
    float3 LightDist    : TEXCOORD1;
    float4 BaseAndBump  : TEXCOORD2;
    float4 Pos          : TEXCOORD3;
    float4 Position     : POSITION;
    float  Fog          : FOG;
};   

// Our simple Diffuse NL Vertex Shader takes the standard Vertex structure as input.
NewShader_Point_Pixel   V_NewShader_Point( Vertex IN)
{
// Declares the structure to fill out and return
    NewShader_Point_Pixel OUT;

// Transforms (multiplies) the texture coordinate by the tiling values for Diffuse and Bump maps, 
// and stores these in the appropriate output vars to pass to Pixel Shader
    OUT.BaseAndBump.xy = Transform(IN.TexCoord0,float2(NewDiffuseUTile, NewDiffuseVTile));
    OUT.BaseAndBump.zw = Transform(IN.TexCoord0,float2(NewBumpUTile, NewBumpVTile));

// Located in Types.h, this handy function sets the LightTangent and LightDistanace based on 
// the passed Vertex structure,  and the Light vars defined in Types.h   
    CalculateLighting(IN,OUT.LightTangent,OUT.LightDist);
  
// Transforms the Vertex into view space
    OUT.Position   = mul(IN.Pos,mWorldViewProjection);

// We also want to retain this transformation for access in the Pixel Shader, why we do so is described below in the Pixel Shader
    OUT.Pos         = OUT.Position;

// No fog, a more complex shader would just apply per-pixel fog in the Pixel Shader using the appropriate Types.h function
    OUT.Fog         = 1;;

// return our fully filled structure to pass to the Pixel Shader
    return OUT;   
}  
 
// Take the Vertex Shader's output structure as input
float4  P_NewShader_Point( NewShader_Point_Pixel   IN ) : COLOR 
{
// Select the Diffuse pixel color according to the current diffuse texture coordinate
   float4 Diffuse = tex2D(sNewDiffuse, IN.BaseAndBump.xy);

// Select the Bump pixel color according to the current bump map texture coordinate
   float4 Bump    = tex2D(sNewBump, IN.BaseAndBump.zw);

// All normalizations and length-dependent calculations should be done in the Pixel Shader, 
// not the Vertex Shader, so that per-pixel length values are employed
   float3 L   = normalize(IN.LightTangent);

// Returns the Normal in tangent space (the R and G components of the bump map equate to X and Y Vector components)
   float3 N    = GetNormal(Bump);

// The dot product of the LightTangent and the Normal determines a factor of the brightness of the light on the surface
// Saturate to avoid negative values (a surface facing away from the incident Light should not be lit at all)
   float  NL   = saturate(dot(N,L));
   
// Multiply out our values, factoring in attenation  
// the GetAttenuation function returns a clamped 0 to 1 attenuation based on a Light's spherical radius, 
// 1 being at the center of the Light and 0 being anywhere beyond its radius.
   float4 color = float4(Diffuse.rgb*LightColor.rgb*NL*GetAttenuation(IN.LightDist),1);
   
// We use IN.Pos view-space transformation to get the corresponding pixel in the HDR Color Buffer to do a manual color blend, 
// since HDR Alpha Blending is not supported in ps 2.0. bHDRBlend, set by the Engine, is only true on secondary passes.
   if(bHDRBlend)
      color = color + GetColorBuffer(IN.Pos);
      
  // return the final color and we're done!
   return color;
}                          

// This technique name is what you'll see listed in Reality Builder as a selectable technique when you open the shader fx file
technique NewShaderPoint
{
   pass p0
   {
      alphablendenable = (0);
      AlphaTestEnable    = (0);
      SrcBlend      = (SourceBlend);
      DestBlend     = (DestBlend);

      vertexshader    = compile vs_2_0 V_NewShader_Point();
      pixelshader     = compile ps_2_0 P_NewShader_Point();
    }
}  

After going through the process of creating a simple shader like this, then applying it to a mesh using RealityBuilder's Asset Browser or the MaxShaderPlugin, you can move on to create more complex shaders. Some things you may wish to use are more texture samplers, cubemaps, 3D volume textures, framebuffer blending & distortion, Spherical Harmonics, different lighting methods. Programming of such advanced shaders will be detailed in subsequent additions to this tutorial, though examining the standard .fx files is a good way to get familiar with the various techniques used in Reality.

Understanding & Creating PostProcess FX

PostProcess FX are pixel shaders that are run on the entire screen (which is actually a texture, drawn on a quad) to alter how the final image looks after the World itself is rendered. Examples of PostProcess FX include blur, depth of field, posterize, black & white, artificial grain, edge detect, and others. On one level, they are similar to "filers" you might use in an application like Photoshop.

In Reality, however, PostProcess shaders can do much more than simple image filtering, since they can store additional renders (or post-fx) in extra Render Targets and texture samplers, and combine or use data from these multiple renders to generate the final image drawn to the screen. Furthermore, unlike static Photoshop filters, PostProcess FX are designed to have their parameters adjusted in real-time so that they can react to in-game events, such as a player's view getting blurred when he takes a heavy hit or smoothly transitioning posterized grainy green when night vision is activated. By understanding how to create, stack, and dynamically manipulate the parameters of PostProcess FX, you can make your game look very distinct. More to come.

Attachment sort Action Size Date Who Comment
SettingThePaths.JPG manage 60.2 K 15 Aug 2004 - 20:52 Main.guest  
shader_mix.jpg manage 22.0 K 16 Aug 2004 - 13:51 Main.guest  
shader_anim.jpg manage 26.8 K 16 Aug 2004 - 14:10 Main.guest  
shader_hdr.jpg manage 16.2 K 16 Aug 2004 - 14:36 Main.guest  
shader_selfillum.jpg manage 17.4 K 16 Aug 2004 - 14:47 Main.guest  
shader_refract.jpg manage 21.8 K 16 Aug 2004 - 15:27 Main.guest  
shader_blur.jpg manage 16.4 K 16 Aug 2004 - 15:35 Main.guest  

ShaderWriting   Edit | Attach | Ref-By | Printable | Diffs | r1.8 | > | r1.7 | > | r1.6 | More