In-depth :: PixelShaders
>> root\raw\shader_bin\shader_src

Table of Contents

Preprocessors :: Constants :: Input-Output :: General Information

PixelShaders are programmable functions in the rendering pipeline, that get executed for every pixel of a mesh.
The PixelShader will calculate the color of each pixel, after the model was transformed from 3D model-space to 2D projection-space (screen space) via the VertexShader. Advanced per-pixel-lighting and diverse texture operations like filters, multi-texturing, bump/normal-mapping or screen-space effects can be implemented using PixelShaders.

1. Preprocessors

PixelShaders, just like VertexShaders, need specific preprocessors at the beginning, so that global-shader-constants, defined within shader_vars.h, can be mapped correctly.
For that to work, you obv. have to include shader_vars.h too.

/* snippet */

#define PC
#define IS_VERTEX_SHADER    0
#define IS_PIXEL_SHADER     1 // <
#include <shader_vars.h>

2. Constants > root\raw\shader_bin\shader_src\shader_vars.h

Shader constants, just like Code-Textures or Code-Variables, are exposed by code and pre-defined.
Most of them are read-only and used to hold general data like:

matrices, time, lighting-information, fog-information, screen-size, parameters assigned within your technique, samplers …

Some constants defined in shader_vars.h can only be used within VertexShaders (constants explicitly registered via VERTEX_REGISTER( index )).
Note: Some of them still work within PixelShaders (eg. renderTargetSize). What this should tell you is, that shader_vars.h is not
a stock file and not 100% correct. Using matrices within PixelShaders will def. throw an error … trust me, I tried.

3. Input and Output - structs

The PixelShader input-struct has to include all semantics found in your VertexShader output-struct. So if your VertexShader output-struct was:

/* snippet */

struct VertexOutput
{
    float4 position : POSITION;
    float2 texcoord : TEXCOORD0;
};

Your PixelShader input-struct has to be:

/* snippet */

#include <shader_vars.h>

struct PixelInput   // could be any name really, as we pass our variables using SEMANTICS
{
    float4 position : POSITION;  // same as vertex output
    float2 texcoord : TEXCOORD0; // ^
};

You don’t really need a PixelShader output-struct, as a PixelShader only outputs a color. See » [PixelShader/Output]

I'll use one for the sake of this tutorial:

/* ... */
#include <shader_vars.h>

struct PixelInput 
{
    /* ... */
};

struct PixelOutput  
{
    float4 color    : COLOR;
};

using structs requires your ps_main function to return a completely initialized output struct:

(this example is assuming that you are using a colorMap assigned to “colorMapSampler”)

Example:

/* ... */
#include <shader_vars.h>

struct PixelInput
{
    float4 position : POSITION;
    float2 texcoord : TEXCOORD0;
};

struct PixelOutput   
{
    float4 color    : COLOR;
};

PixelOutput ps_main( const PixelInput input )  // ps_main returns the output-struct; we also reference our input-struct as "input"
{
    // create the output-struct object
    PixelOutput fragment;  

    float  pulseSpeed = 0.25f;
    float3 textureSample;

    // sample the texture assigned to colorMapSampler using the current uv's
    textureSample = tex2D(colorMapSampler, input.texcoord).rgb;

    // animate the red channel of the sampled texture (pulsing)
    textureSample.r = clamp((textureSample.r * ((gameTime.x * pulseSpeed) + 1.0f / 2.0f)), 0.0f, 1.0f);

    // the returned color has to be a float4
    fragment.color = float4(textureSample, 1.0f);

    // return the struct
    return fragment;
}

if you do not want to use a struct for the PixelShader output:

Example:

/* ... */
#include <shader_vars.h>

struct PixelInput
{
    float4 position : POSITION;
    float2 texcoord : TEXCOORD0;
};

float4 ps_main( const PixelInput input ) : COLOR  // ps_main returns a float 4 assigned to the COLOR Semantic
{
    float4 myColor;

    /* color calculations ... */
    
    // simply return a float4 
    return myColor;
}

4. General information

As I stated before, always make sure that each and every input you defined contributes to the final color that gets output by your shader.
Microsofts shader compiler tends to optimize your shader and removes everything thats unused, so only only assigning your input to a local variable that is not used for your calculations is not enough.

If you don't feel like removing inputs, for testing purposes, heres a small little trick:

/* ... */
#include <shader_vars.h>

struct PixelInput
{
    float4 position : POSITION;
    float2 texcoord : TEXCOORD0;
    float4 unused   : TEXCOORD1;
};

float4 ps_main( const PixelInput input ) : COLOR  // ps_main returns a float 4 assigned to the COLOR Semantic
{
    float4 myColor;

    /* color calculations not using input.unused ... */
    
    myColor.a = myColor.a + (input.unused.x * 0.00001f); // this wont change a thing but will stop linker from throwing errors :)

    // simply return a float4 
    return myColor;
}

Techniques :: VertexShader