# HxSL - Haxe Shader Language

Today hardware accelerated 3D rendering is using "shaders" in order to perform many tasks :

*vertex shaders*are used for transforming and projecting each geometry point into 2D space, and set up "variables" that will be interpolated on a per-pixel basis and can be used by the pixel shader*fragment shaders*are used for blending different textures and colors into a single pixel color that will be written to screen

Thanks to Macros, we were able to develop a high level shader language called HxSL that uses Haxe syntax and can directly be embedded into Haxe programs source.

# Shader expression

The syntax of the shader follows the syntax of Haxe language, however only a subset of the language is supported. Here's a HxSL shader expression example :

var input : { pos : Float3, uv : Float2 }; var tuv : Float2; function vertex( mpos : Matrix, mproj : Matrix ) { out = input.pos.xyzw * mpos * mproj; tuv = input.uv; } function fragment( t : Texture ) { out = t.get(tuv,wrap); }

A shader expression consists in the following :

- an
`input`

object, which declares the data that is stored into the Vertex buffer - zero or more
`variables`

that will be written to by the vertex shader, then interpolated by the GPU and read by the fragment shader - one vertex shader function, with its
`parameters`

(constants for all the vertexes processed) - one fragment shader function, with its
`parameters`

(constants for all the pixels processed)

The following variable types are defined :

`Float`

: a single scalar value`Float2`

,`Float3`

,`Float4`

: a set of 2,3,4 floats`Matrix`

or`M44`

: a 4x4 matrix of floats`M33`

,`M43`

and`M34`

: the corresponding matrix`Texture`

: a 2D texture`CubeTexture`

: a cubic texture (6 faces)`Int`

(or`Color`

) : a 32 bit integer, converted to a Float4 with each float in the range [0.0, 1.0]

Each shader will write to the `out`

variable, which has the type `Float4`

# Local variables

It is possible to declare local variables in a HxSL shader. They can be either declared with an initial value :

function vertex(...) { var tpos = input.pos.xyzw * mpos; out = tpos * mproj; }

Or they can be declared and initialized later, in which case it is necessary to declare their type as well:

function vertex(...) { var tpos : Float4; tpos = input.pos.xyzw * mpos; out = tpos * mproj; }

Even when declaring an initial value, the type may also be explicitly declared for clarity.

# Constants

Constants can be a single scalar value or a group of constant values declared as a Haxe array :

0.5 // Float [0.5,1.5] // Float2 [1,2,3] // Float3 [1,2,3,4.5] // Float4

# Operations

Here's the list of operations allowed in HxSL. FloatX means that this can apply to `Float`

, `Float2`

, `Float3`

and `Float4`

values, where all must be of the same type.

All operations can be used in two ways :

// standard C-like way : add(x,y) // Object-Oriented way : x.add(y)

`add`

(or`+`

) : add two valuesfunction add( a : FloatX, b : FloatX ) : FloatX;

`sub`

(or`-`

) : subtract two valuesfunction sub( a : FloatX, b : FloatX ) : FloatX;

`mul`

(or`*`

) : multiply two value components, or project a vector, or multiply two matrixesfunction mul( a : FloatX, b : FloatX ) : FloatX; function mul( a : Float4, m : Matrix ) : Float4; function mul( a : Matrix, b : Matrix ) : Matrix;

`div`

(or`/`

) : divide two valuesfunction div( a : FloatX, b : FloatX ) : FloatX;

`mod`

(or`%`

) : calculate the remainder of a divisionfunction mode( a : FloatX, b : FloatX ) : FloatX;

`pow`

: calculate a^b for two valuesfunction pow( a : FloatX, b : FloatX ) : FloatX;

`min`

: calculate the minimum for two valuesfunction min( a : FloatX, b : FloatX ) : FloatX;

`max`

: calculate the maximum for two valuesfunction max( a : FloatX, b : FloatX ) : FloatX;

`dot`

(or`dp`

,`dp3`

,`dp4`

) : calculate the dot product of two vectorsfunction dot( a : Float3, b : Float3 ) : Float; function dot( a : Float4, b : Float4 ) : Float;

`cross`

(or`crs`

) : calculate the cross product of two vectorsfunction cross( a : Float3, b : Float3 ) : Float3;

`neg`

(or unary`-`

) : calculate the negative of a valuefunction neg( v : FloatX ) : FloatX;

`inv`

(or`rcp`

, or`1 / x`

) : calculate the inverse of a valuefunction inv( v : FloatX ) : FloatX;

`sqrt`

(or`sqt`

) : calculate the square root of a valuefunction sqrt( v : FloatX ) : FloatX;

`rsqrt`

(or`rsq`

, or`1 / sqrt(x)`

) : calculate the inverse square root of a valuefunction rsqrt( v : FloatX ) : FloatX;

`log`

: calculate the logarithm of a valuefunction log( v : FloatX ) : FloatX;

`exp`

: calculate the exponent of a valuefunction exp( v : FloatX ) : FloatX;

`length`

(or`len`

) : calculate the length of a valuefunction length( v : FloatX ) : Float;

`normalize`

(or`norm`

, or`nrm`

') : return the normalized vector of a valuefunction normalize( v : FloatX ) : Float3;

`sin`

: calculate the sine of a valuefunction sin( v : FloatX ) : FloatX;

`cos`

: calculate the cosine of a valuefunction cos( v : FloatX ) : FloatX;

`abs`

: calculate the absolute of a valuefunction abs( v : FloatX ) : FloatX;

`saturate`

(or`sat`

) : calculate`min(1,max(v,0))`

of one valuefunction saturate( v : FloatX ) : FloatX;

`frac`

(or`frc`

): calculate the fractional portion of one valuefunction frac( v : FloatX ) : FloatX;

`int`

: calculate the integer portion of one valuefunction int( v : FloatX ) : FloatX;

`lt`

(or`<`

) : return (a < b) ? 1 : 0 for each valuefunction lt( v : FloatX ) : FloatX;

`lte`

(or`<=`

) : return (a <= b) ? 1 : 0 for each valuefunction lte( v : FloatX ) : FloatX;

`gt`

(or`>`

) : return (a > b) ? 1 : 0 for each valuefunction gt( v : FloatX ) : FloatX;

`gte`

(or`>=`

) : return (a >= b) ? 1 : 0 for each valuefunction gte( v : FloatX ) : FloatX;

- "eq" (or "==") : return (a == b) ? 1 : 0 for each value
function eq( v : FloatX ) : FloatX;

- "neq" (or "!=") : return (a != b) ? 1 : 0 for each value
function neq( v : FloatX ) : FloatX;

`kill`

: only available in fragment shader. Skip a pixel write if the value is < 0function kill( v : Float ) : Void;

`get`

: access a texture color value using a given texture coordinates (see below for examples)function get( t : Texture, v : Float2, ....flags ) : Float4; function get( t : CubeTexture, v : Float3, ....flags ) : Float4;

`transpose`

: transpose a matrixfunction transpose( m : MXY ) : MYX;

# Texture Access

Texture access is done using the `get`

operation and can take one or several flags :

function fragment( tex : Texture ) { out = tex.get(uv, wrap, nearest); }

The following texture flags are allowed :

`wrap`

: wrap around the texture borders`clamp`

: stick to the texture borders`nearest`

: round-to-nearest pixel read`linear`

: bilinear pixel read`mm_no`

: disable mipmapping`mm_nearest`

: round-to-nearest mipmapping`mm_linear`

: linear mipmapping`lod(v)`

: texture lod (for mipmapping)

**new in hxsl 2.0**

If you want to have a texture flag that depends on a variable, you can use the following :

`wrap=<value>`

: a Bool value that tells if we wrap or clamp`filter=<value>`

: a Bool value that tells if we use linear filter or no (nearest) filter`mipmap=<value>`

: null for no mipmapping, true for linear, false for nearest`lod=<value>`

: the specified lod bias is applied

# Swizzling / Masking

It is possible to read only a part of a value, or to rearrange the elements of a value by using *swizzling*. For example :

var tmp : Float4 = [1,2,3,4]; out.x = tmp.x; // copy 1 into x out.xz = tmp.yz; // copy 2 into x and 3 into z out = tmp.xxww; // copy 1 into x and y and 4 into z and w

The practice of only writing some parts of a value is called *masking*.

You can only swizzle on components which are accessible, for instance you cannot read the `z`

value of a `Float2`

variable. The only exception is for input and non-temporary variables (see Variable Types, below), where all 4 components can be read, as they are always filled with 0 if not written.

## Auto-Scalar Swizzling

Operations which take two `FloatX`

values, and return a FloatX, accept a single `Float`

as one of the two values. HxSL will automatically swizzle this value, converting it to the necessary FloatX type, with the original float used for each value within that type. For instance :

var tmp : Float4 = [1,2,3,4]; out = tmp * 0.5; // will multiply all components by 0.5

This also works for any single Float value :

var half : Float = 0.5; out = half * tmp; // same as writing half.xxxx * tmp

# If / Else

If/Else can be used inside a value-expression as follows :

color = if (red < 0) 1 else 0.2;

This will actually compile to code corresponding to the following :

color = (red < 0) * 1 + (red >= 0) * 0.2;

However, you cannot write that directly, as you can only use comparison operators inside a if/else expression.

## If/Else statements

**new in hxsl 2.0**

You can also use if/else statements, a specific version of the shader will be compiled automatically based on the result tests :

function fragment( colorEffect : Float4 ) { // ... if (colorEffect != null) color *= colorEffect; // ... }

# For Loops

It is also possible to use for loops in HxSL :

// Make the sum of 3x3 texture samples. var color = [0,0,0]; for (x in -1...2) for (y in -1...2) color += tex.get(uv + [x,y]);

For-loops will be *unrolled* so the shader will be the same as if you would have written the corresponding 9 lines for each of the (x,y) possible values.

In HxSL v1 only constant values are allowed. In HxSL v2 a specific version of the shader is built when the values are changed.

# Helper Functions

You can define helper functions before `vertex`

and `fragment`

shaders. They can take several arguments and will `return`

a result value. In practice, helper functions are *inlined* in the compiled shader.

// helper function example : function lerp( x : Float, y : Float, v : Float ) { return x * (1 - v) + y * v; }

[It is unclear whether the return type can be specified in the function declaration.]

# Include

It is possible to have extern files containing any HxSL declarations. Such extern files can be included in any shader with the `include`

statement :

include('baseShader.hxsl'); // only define the fragment shader function fragment( t : Texture ) { out = t.get(tuv,wrap); }

# Matrix Transposition

In order to perform operations such as vector projection or matrix multiplication, it is necessary to have the right matrix *transposed* (that is to be able to read its columns instead of its lines). This is because a Matrix actually consists of four Float4 values, which can represent either the matrix lines (by default) or its columns (transposed mode).

HxSL performs what is called *matrix transposition inference*. It infers from the operation you are using in which mode (either transposed or not) the matrix should be.

For instance when projecting a vector :

function vertext( mpos : Matrix ) { var tpos = pos * mpos; ... }

The will force the matrix `mpos`

to be transposed. Any further operation using `mpos`

in a not-transposed way will cause an error.

## Row Access

You can access a matrix row vector with `m[3]`

, this will for example return the 4th row of the matrix.

# Indirect Access

You can also use indirect access in HxSL by declaring some Array-variables, such as the following :

var input : { indexes : Float2 } function vertex( pos : Float4<10> ) { out = pos[input.indexes.x] + pos[input.indexes.y] }

Array variables must be of constant size.

Please note that if you are indexing a Matrix Array, your indexes needs to be multiples of 4 : 0 will reference the first matrix and 4 the second one.

# hxsl.Shader

**new in 2.0**

Thanks to the powerful Macros it's possible to embed a HxSL shader into an Haxe class this way :

class MyShader extends hxsl.Shader { static var SRC = { var input : { pos : Float3, uv : Float2 }; var tuv : Float2; function vertex( mpos : Matrix, mproj : Matrix ) { out = ((mpos == null) ? input.pos.xyzw : input.pos.xyzw * mpos) * mproj; tuv = input.uv; } function fragment( tex : Texture ) { out = tex.get(tuv); } }; }

You can then create a shader with `new MyShader()`

, set its variables and when ready to render call the `getInstance()`

method. It will take care of compiling and caching the shader, building the buffers of constants, etc. Look at the returned instance fields and at the h3d Engine.hx implementation for more details.

## Variable Types

Among the variables used as part of an HxSL shader, we distinguish the following types of variables :

`input`

: the input variables are the ones that come from the vertex buffer`const`

: a constant variable that is set once and available in either vertex or fragment shader (mproj in our example). They do not vary for the scene being currently rendered: something like a light position would be a good example. If you are coming from another shader language, you may know of these as "Uniforms".`param`

: a optional constant, that might not be used depending on the shader specialized version (mpos in our example)`var`

: a varying variable, that is set in vertex shader and read in the fragment shader`texture`

: a texture variable (tex in our example)`tmp`

: a temporary variable, used for locals

The `hxsl.Shader`

class will pre-allocate all the `const`

variables, but not the `param`

variables, since these can be set to `null`

to disable some effects.

# Using HxSL with Flash11

In order to use shaders with the Flash11 API, you can simply declare a new shader, as in following example :

// was format.hxsl.Shader for HxSL v1.0 class MyShader extends hxsl.Shader { static var SRC = { var input : { pos : Float3, uv : Float2 }; var tuv : Float2; function vertex( mpos : Matrix, mproj : Matrix ) { out = input.pos.xyzw * mpos * mproj; tuv = input.uv; } function fragment( t : Texture ) { out = t.get(tuv,wrap); } } }

This will generate methods for your shader that will perform all the necessary setup of shader constants, and select the shader program for your 3D context. If using multiple shaders, use `unbind`

to disconnect the first one before using `bind`

on the second one; this will allow each shader to use a different set of texture parameters.

You can find all details and examples here : Using Flash 3D API