Oh hai

or

Basic texturing

And

Multiple outputs


Construction started


Before we begin, let’s talk about how structs serve us in HLSL.

Well, you probably guessed that sometimes we will want to return more than a single value from the programs. In order to do so, we usually use IN and OUT structs. (* IN and OUT not being keywords, just emphasis on my transmission).

Take a look at the following declarations:

// TEXCOORD[n] semantic will be explained shortly
struct VertexIn
{
	float4 pos    	: POSITION;
	float2 texco    : TEXCOORD;
};

struct VertexOut
{
	float4 pos    	: POSITION;
	float2 texco   	: TEXCOORD;
};
// structs are accessed like in C ([name].[field] = [new value] )

in this example, I defined two structs: VertexIn and VertexOut, serving as input data for vertex program and its output object, respectively.

It is somewhat accepted to use the same struct for vertex program output and pixel program input, since this follows the rendering flow in logic. But be warned, most of vertex output semantics are NOT acceptable as pixel input. You can only share VS output and PS input if you are not attempting to access variables using vertex output semantics in the pixel shader.

Input and output structs can only contain accepted input and output semantics, other things (such as the shared matrices) must be passed as uniform.

Take a careful look at the VS and PS in/out semantics. Review this link when you write your own shaders:

http://msdn.microsoft.com/en-us/library/bb509647%28VS.85%29.aspx

My textures, let me show you them


Using the two structs we wrote above, we will start writing a simple texturing shader, and a few simple texture manipulating shaders.

First of; how do we use our new structs as input and output for our programs?
It goes like this:

// remember to add the World-View-Projection matrix when exporting to OGRE
VertexOut mainVS(VertexIn input)
{
	VertexOut output = (VertexOut)0; // initialize with 0 values all over
	// put values to output before returning it; 
	return output;
}

float4 mainPS(float2 uv : TEXCOORD ) : COLOR 
{
}



Though it is not needed for our current program, I provided this example of struct usage.

Sampler


The second thing we will need before we can use a texture is an image sampler. A sampler is use to determine the specific color in a specific point along the mash.

There are a few types of samplers; 1d, 2d, 3d and cubic (and something else that I forgot).
For this program we will use the sampler2D.

Creating the sampler:

We will have to define two things: a texture and the sampler.
Important note: Sampler2D and sampler2D are not the same!

Texture [name];

// dx9 style declaration
sampler2D [name] = 
sampler_state
{
    	Texture = < [name you defined] >;
};


Load a texture for your material;
After you define the texture and compile, the texture will appear on the material properties window (right side of the screen). Add some picture, preferably a lolcat. My texture is called xtex. Clicking on the value will allow you to load a picture.
Image

Changes:

The pixel program (first time since we started) now gets input. It receives a texture coordinates (a float2: x + y or u + v, depends on who you ask) requested by the drawing loop.

Now we need to use this coordinates with out sampler to draw the texture to the object; in order to do so we use a function called tex2D.
Its input is: ( sampler-state, {LEX(pagename="UV Coordinates")}texture coordinates{LEX} ), and return the color value of that specific point of the picture.
Image
…Result on a plane

That’s about it. Now to complicate things…

If you zoom in very close or out very far (or just look at the picture) you will notice it doesn’t mipmap nor does it interpolate values. The result is a very sharp edged texture.

In order to change that we can set some basic sampler option.
In the following I defined using a linear interpolation function for the sampler.

// dx9 style declaration
sampler2D [name] = 
sampler_state
{
    	Texture = < [name you defined] >;
    	MipFilter = LINEAR;
    	MinFilter = LINEAR;
    	MagFilter = LINEAR;
};


Image Image
Before and after… left side uglier – srsly

Exporting to ogre

Although you do know how to move your code to OGRE already, there are a few things we must discuss.

The sampler

You cannot declare the sampler as we did earlier when we move the code to OGRE. We need to pass it as uniform.
…uniform sampler2D |name|…
…In PS input. (You will not need to pass it from the material however, OGRE will handle that)

 You know what box it is…
Feeling smart?

Want to learn more about samplers?
http://msdn.microsoft.com/en-us/library/bb509644%28VS.85%29.aspx

The material

You will also need to add a texture unit to your pass:

…………
		pass
		{
			texture_unit
			{
				texture [texture name + extension] 2d
				// can also be 1d,3d or cubic       ^
			}
…………

About the input and output

Note that you can numerate all input and output semantics (such as TEXCOOR1)
This is used when more than one data is passed with the same semantic.

This is important because data kept in TEXCOORn (for instance) will be the same data everywhere in the same execution of the shader (per vertex/pixel run). This means you don’t need to ‘physically’ pass things from vertex to pixel programs like you do when you share structs.

Unlike in FXC, in OGRE, if you do not set TEXCOORD in the VS, it will be blank in the PS. (FXC spoils you here). Therefore all TEXCOORD data you wish to use in the PS will have to be return in the VS, even if not ‘physically’ linked with same struct.

first option: shared structs

struct VertexIn
{
	float4 pos    	: POSITION;
	float2 texco    : TEXCOORD0;
};

struct VertexOut
{
	float4 pos    	: POSITION;
	float2 texco    : TEXCOORD0;
};

VertexOut mainVS(          VertexIn input, 
   	   	   uniform float4x4 worldViewProj_m)
{
	VertexOut output = (VertexOut)0;
	output.pos = mul( input.pos, worldViewProj_m );
	output.texco = input.texco;
	return output;
}

float4 mainPS(    	VertexOut input, 
   	   	uniform sampler2D image 
   	     ) : COLOR 
{
	return tex2D(image, input.texco);
}


While valid in all accounts, it can be volatile (since PS cannot accept POSITION semantic). You can pass it this way, but trying to use the VS output data that isn’t valid in PS will cause a critical error (as happens to many novice HLSL users trying to pass normals for the first time).

Second option: separate structs

NOTE: for teaching purposes, we will use this style throughout part one because it’s very organized and emphasize what can or cannot be sent from vertex program to pixel program.

struct VertexIn
{
	float4 pos    	: POSITION;
	float2 texco    : TEXCOORD0;
};

struct VertexOut
{
	float4 pos    	: POSITION;
	float2 texco    : TEXCOORD0;
};

struct pixelInput
{	
	float2 texco    : TEXCOORD0;
};

VertexOut VS(           VertexIn input, 
   	   	uniform float4x4 worldViewProj_m
   	    )
{
	VertexOut output = (VertexOut)0;
	output.pos = mul( worldViewProj_m, input.pos );
	output.texco = input.texco;
	return output;
}

float4 PS(    	   pixelInput input, 
   	   uniform sampler2D image
   	 ) : COLOR0
{
	return tex2D( image, input.texco );
}


We create separate structs for pixel and vertex, and use TEXCOOR0 as a bridge – it is set in the vertex program and used in the pixel one.

Although it’s the most organized method (to the eyes), it can get smaller shaders to use a lot of unneeded length.

Third option: in, out and inout keyword

You either love and use it solely, or ignore it completely. You can define input arguments as either in, out or inout.

In that method, you can define your programs with no return value (void). While appealing without being volatile, it might cause some confusion and a very lengthily input list. Here’s how it goes:

This method is somewhat popular with GLSL users that use HLSL as well, since GLSL works like this.

In arguments are just like any input you used so far, but will not be passed forward.
Out arguments are received blank, and will be passed forward.
Inout arguments are like in, but are passed forward like outs.

Note that both out and inout must be declared as input argument as well.

void VS( inout   float4   pos    : POSITION, 
  	 inout   float2   texco  : TEXCOORD0, 
  	 uniform float4x4 worldViewProj_m 
)
{
	pos = mul( worldViewProj_m, pos );
}

void PS( in    	 float2    texco    : TEXCOORD0, 
   	 out     float4    finColor : COLOR, 
   	 uniform sampler2D image
)
{
	finColor = tex2D(image, texco);
}



Each one of these methods has its ups and downs, choose to your liking.
But again, pick one and stick with it.