My Name is AGAL, and I Come from Adobe – Part1
Imagine some day that humanity becomes extinct.
Imagine that after our extinction all paper documentation, over time, becomes worn and unreadable. Imagine then some alien species finding a DVD or a solid state memory, that was built by humans, containing some sort of information.
How would they decipher it? How could they find the key to understand what information is contained within the “artifact”?
More generally, if they got in touch with the results of our technology, how would they able to actually learn our discoveries and use our technology, if they needed that technology itself to be able understand it?
Adobe recently introduced a new language called AGAL (Adobe Graphics Assembly Language). It’s part of Molehill, and its purpose is to create the so called “shaders”: tiny little programs that affect how 3d models get rendered into the scene.
Shaders are cool. They allow amazing rendering effects. They’re also much harder to code than regular Actionscript.
This is what AGAL looks like:
//vertex shader m44 op, va0, vc0 // pos to clipspace mov v0, va1 // copy uv //pixel shader tex ft1, v0, fs0 <2d,linear,nomip> mov oc, ft1
Doesn’t it look like hieroglyphics? What’s the key to access it?
The problem is that AGAL is very little documented at this stage. Let me try to cast some light on this mysterious shading language.
Every line of the shader is a command, specified by a 3 character string called “opcode”.
The syntax of an AGAL code line is the following:
<opcode> <destination> <source 1> <source 2 or sampler>
This is the key. Keep this syntax in mind and AGAL will suddenly stop looking like an unreadable blob.
Following the opcode, depending on the command there can also be a destination, and one or two sources. The destination and the sources are “registers”: small memory areas in the GPU for use by shaders (more on registers below).
AGAL features about 30 different opcodes. The full list of available opcodes can be found in the Molehill documentation. Here are some of the most common opcodes.
- mov move data from source1 to destination, component-wise
- add destination = source1 + source2, component-wise
- sub destination = source1 – source2, component-wise
- mul destination = source1 * source2, component-wise
- div destination = source1 / source2, component-wise
- dp3 dot product (3 components) between source1 and source2
- dp4 dot product (4 components) between source1 and source2
- m44 multiplication between 4 components vector in source1 and 4×4 matrix in source2
- tex texture sample. Load from texture at source2 at coordinates source1.
Registers are small memory areas in the GPU that AGAL programs (shaders) can use during their execution. Registers are used both for the sources and for the destination of AGAL commands.
You can also pass parameters to your shaders through these registers.
Each register is 128 bits wide, meaning it contains 4 floating point values. Each of these values is called a “component” of the register.
Register components can be accessed both with through the “coordinate” accessors (xyzw), and through the color accessors (rgba).
So, the first component of a register, can be accessed both with:
Some of the opcodes above, like “add”, perform their operations “component wise”. This means that the addition operation is performed component by component.
These are six types of registers available.
1. Attribute Registers
These registers reference to the input VertexBuffer of the vertex shader. Therefore they are only available in vertex shaders.
In order to assign a VertexBuffer to a specific attribute register, use the function Context3D:setVertexBufferAt(), with the proper index.
Then from the shader, access the attribute register with the syntax: va<n>, where <n> is the index number of the attribute register.
There are a total of 8 attribute registers available to vertex shaders.
2. Constant Registers
These registers serve the purpose of passing parameters from Actionscript to the shaders. This is done through the Context3D::setProgramConstants() family of functions.
These registers are accessed from the shader with the syntax: vc<n>, for vertex shaders, and fc<n> for pixel shaders, where <n> is the index number of the constant register.
There are 128 constant registers available to vertex shaders, and 28 constant registers for pixel shaders.
3. Temporary Registers
These registers are available to shaders, that can use them for temporary calculations.
They are accessed with the syntax vt<n> (vertex) and ft<n> (pixel) where <n> is the register number.
There are 8 of these available for vertex shaders, and 8 for pixel shaders.
4. Output Registers
The Output Registers are where vertex and pixel shaders store the output of their calculation. For vertex shaders this output is a clip space position of the vertex. For pixel shaders it is the color of the pixel.
These registers are accessed with the syntax op, for vertex shaders, and oc, for pixel shaders.
There is obviously only one output register for vertex and for pixel shaders.
5. Varying Registers
These registers are used to pass data from vertex shaders to pixel shaders. The data that is passed gets properly interpolated by the GPU, so that the pixel shader receives the correct value for the pixel that is being processed.
Typical data that gets passed in this way is the vertex color, or the UV coordinates for texturing.
These registers can be accessed with the syntax v<n>, where <n> is the register number.
There are 8 varying registers available.
6. Texture Samplers
The texture sampler register are used to pick color values from textures, based on UV coordinates.
The texture to be used is specified through Actionscript with the call Context3D::setTextureAt().
The syntax for using texture samples is: fs<n> <flags>, where <n> is the sampler index, and <flags> is a set of one or more flags that specifies how the sampling should be made.
<flags> is a comma separated set of strings, that defines:
- texture dimension. Can be: 2d, 3d, cube
- mip mapping. Can be: nomip, mipnone, mipnearest, mipnone
- texture filtering. Can be: nearest, linear
- texture repeat. Can be: repeat, wrap, clamp.
So, for example, a standard 2d texture without mip mapping, and linear filtering could be sampled into temporary register ft1, with the following line:
“tex ft1, v0, fs0 <2d,linear,nomip> “
where the varying register v0 would hold the interpolated texture UVs.
Vertex and Pixel Shader Example Explained
Let’s go back to our shader example, and explain its operation now.
Let’s suppose our vertices in the VertexBuffer contain vertex positions, at offset 0, and texture UVs, at offset 3.
We want our vertex shader to transform the vertex positions into clip space, and to pass on the UVs to the pixel shader.
This is performed with the following code:
m44 op, va0, vc0 // pos to clipspace mov v0, va1 // copy uv
The first line performs a 4×4 matrix multiplication between the vertex input, va0, and the transformation matrix, from model space to clip space, that we assume was written, from Actionscript, in the constant register 0, vc0.
The matrix can be written to the shader with the call:
Context3D::setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, matrix, true );
The second line of the shader, copies the vertex UV data to varying register 0, v0, so that it can get interpolated, and passed to the pixel shader.
The pixel shader has to sample the texture, and copy the color to the output.
tex ft1, v0, fs0 <2d,linear,nomip> mov oc, ft1
So, this vertex/pixel shader pair just transforms the 3d model onto the 2d screen, and makes the texture mapping.
This is it! Our first vertex and pixel shaders.
Cool! Can I Make a Shader Now?
Actually, you can make the shader, but you won’t be able to use it, because we’re still missing the Actionscript side of it.
In part 2 of this tutorial I’ll be diving into the Actionscript required to make use of our shader, and provide code for a full working demo.
I hope this tutorial was helpful to you. If so, please post your comments below.
Stay tuned for more!
Filed under: Shaders
Like this post? Subscribe to my RSS feed and get loads more!