Effect Files
So far we have talked about shaders. After having the ability to write vertex and pixel shaders, the next logical step is to combine these two in one place. The combination of these types of shaders, together with a set of other states that control how the graphic pipeline functions is called an effect.
If you look again at the graphics pipeline, you can imagine an effect as a block that replaces or controls a few of the blocks in the diagram: Vertex Processing, Geometry Processing, Pixel Processing, Texture Samplers, and a part of Pixel Rendering. The Vertex Processing and Pixel Processing are implemented by writing programmable shaders. The other blocks can be controlled by setting the pipeline state, i.e. setting values to a set of specific variables.
Effects also provide a convenient way to write shaders for multiple hardware versions. For this, effects have the concept of techniques. A technique is a collection of one or more passes. Each pass defines a certain way of rendering the object. To do this, a technique and its passes encapsulate global variables, pipeline states, texture sampler states and shader states. This way, you can write different shader versions, which rely on different functionalities from the hardware, and encapsulate them in techniques. Then, at runtime, select the proper technique to be used, based on the hardware the application is running. On a high-end machine you will probably want to use a technique that contains more complex shaders, and on a low-end machine, the simpler and faster one. The shaders are defined as functions, and assigned to two pipeline variables, named VertexShader and PixelShader.
The general layout of an effect file can be seen below.
//parameter declarations […] //data type declarations […] //function declarations […] //technique declarations […]
This probably is not very descriptive, but in the rest of the article, I will explain each element.
Data Types
Let us take a quick look at the data types available in HLSL. After this, in the next session, you will see hoe to declare variables and parameters using these data types. The simplest types are the scalar types, listed below.
| Type | Value |
| bool | true or false |
| int | signed integer |
| half | 16-bit floating point |
| float | 32-bit floating point |
The float type is the native type used inside GPUs, and this is what you will most often use. Because of this, some GPUs that might not support other types (int, half, double) will emulate them using float. Integers usually fall into this category, and because of this emulation, the full range of 32-bit integers will not be covered by a 32-bit floating-point representation.
Right after scalar types, vector types come into play. A vector contains between one and four components of a scalar type. A vector type is composed by a scalar type immediately followed by a number that indicates the number of components of the vector. Some examples include: float3, half2, int4, double2. An advantage of vector types, besides the ease of use, is the fact that operations done on them are applied simultaneously on all components. For example, adding two float4 variables will add the corresponding components in just a single instruction. To access individual components of a vector, you can use two sets of accessors: rgba and xyzw. As an example, for a variable of type float4 named var, writing var.x, or var.r gives access to the first component of var; var.y and var.g to the seconds, and so on. Usually, when using a variable as a position, you would use the .xyzw accessors, and when using it as a color, the .rgba accessors.
The next data structure that is common in computer graphics is the matrix type. A matrix type is defined similar to vectors, but it uses two dimensions. In the definition, the scalar type is followed by the number of rows, an x and the number of columns: int4x2, double1x3, float4x4. Usually, you’ll just use matrices in multiplications, but if you ever need to access a certain component of the matrix, there are a few ways of doing this: a zero based notation, which has the form of an underscore, followed by the letter m, the row number, and then the column number; a one based notation, which has the form of an underscore, followed by the row and column number; and finally access can be done as in an array, by specifying the row and column numbers in square brackets, zero-based. Assuming we have a variable mvar of type float4x4, all the following notations will access the same component, the one on the second row and third column: mvar._m12, mvar._23, mvar[1][2].
A texture type is the data type that represents a texture object. The keyword for this data type is simply texture. Another data type linked to textures is the sampler, which contains a sampler state, i.e. the texture to be sampled, and the filters that should be used. The common sampler types are sampler, sampler1D, sampler2D, sampler3D and samplerCUBE. We will go into more details about using textures and samplers later.
The last important data type is the structure. A structure is a user-defined data type that has a collection of members, of other data types. The keyword struct is used to define a structure, after which the structure name may be used. To access components of a structure, the structure operator (.) is used. An example of a structure that contains two members, a position and a color, can be seen below.
struct demoStruct
{
float3 position;
float4 color;
}
These are the most important data types you will get to use when writing HLSL code.
Intrinsic Functions
HLSL comes with a large set of intrinsic functions (functions defined by the language) that offer access to commonly used functionality. The parameters for these functions depend on what the function does, but they are always matrices, samplers, scalars or vectors. You can see the full set of intrinsic functions here.


by Dennis Brandis, on 11.19.09 @ 12:22 am
On page 4:
float4 PixelShaderFunction(float2 TexCoords : TEXCOORD0) : COLOR0
{
return tex2D(TextureSampler, input.TexCoord);
}
may be it should be:
return tex2D(TextureSampler, TexCoord);
by Catalin Zima, on 11.19.09 @ 2:16 pm
I fixed it now. Thanks!
by Crash Course in HLSL « optic rust, on 01.02.10 @ 4:43 pm
[...] What does HLSL stand for? Why was it created? How does an HLSL effect file look like? What can you do with HLSL? Catalin Zima answers these questions and more in a brilliant article introducing HLSL over at her site. Check it out here [...]
by Hassan Aly Selim, on 01.31.10 @ 8:42 pm
Thanks alot for this HLSL Crash Course =)
Now I can start writing my own Custom Shaders in XNA !
by Catalin’s XNA Experiments » Re-awarded XNA/DirectX MVP for 2010, on 04.03.10 @ 6:10 pm
[...] Crash Course in HLSL (also published [...]
by Enio, on 05.01.10 @ 3:12 pm
Thanks for your helpful explanations. Which book would be more suitable for a beginner who needs to learn everything from scratch (shaders, algebra, algorithms, and physical)? Thanks!
by Enio, on 05.02.10 @ 12:41 am
Why not develop the second part of the course with several practical examples (Blur, DOF, Sepia, etc.)?
by Crash Course in HLSL, on 06.17.10 @ 8:54 am
[...] Can be found at: http://www.catalinzima.com/?page_id=575 [...]
by Devrunner, on 07.27.10 @ 11:50 am
You’re the best.