At FIEA, my first group project was an Adobe Animate (formerly known as Flash) game. To get myself acclimated with Flash development, I decided to do a little project before production started. I had read an awesome blog post that shows off Stage3D and AGAL (Adobe Graphics Assembly Language). Making something using Stage3D and AGAL seemed like a good way to get my feet wet with this new platform.
There is definitely a lack of resources on AGAL. At its core, it’s a shading language, and its output is similar to that of GLSL. Functionally, however, it acts as an assembly language, so there’s registers and op codes and all that other fun low-level stuff. As for Stage3D, it isn’t much different than using OpenGL, so if you’re familiar with that then you’ll be able to follow along without much issue.
Before starting, the project files for the post can be found here. It contains the AGALMiniAssembler that will compile the AGAL into usable bytecode for the shader, as well as the source for everything I’ll be going over. I’d recommend keeping it open while you read the article.
My project is split into three source files:
DrawTexturedSprite.as, which is the document class for the application and is called when the game is started.
ShaderObject.as, which creates the shader program, loads the AGAL source, creates vertex buffers, and can optionally load a texture into the shader.
CustomSprite.as, which extends
ShaderObjectand adds functionality for scaling, rotating, and positioning the object relative to the stage.
Creating the Context
Let’s start in
DrawTexturedSprite.as. In our document class constructor, we’re going to set up two event listeners. The first will initialize our render loop, and the second will request a 3D context from
When the context is created, we set up the back buffer and create our sprite. It will be placed in the middle of the stage, have a width and height of 200 pixels, and will be rotated 45 degrees.
Finally, in our
Render function, we’ll clear the screen, draw the sprite to the context, then display the context on the screen.
Now let’s move on to the rendering!
The first thing we need to do is load our shaders!
Next, we assemble the shaders using the
AGALMiniAssembler and create our shader program. We pass vertex, uv, and index data in and use it to create buffers. We set the vertices to attribute location 0 and uvs to attribute location 1 (this will be important for later).
To draw, we set the program to the current object’s program and then draw the index buffer.
So far, this is very similar to OpenGL. Let’s look at using textures.
Like the shader code, the first step is to load the texture.
Next, have the context create a texture and upload the data to that texture. We’ll be storing this texture at texture index 0.
The last step is to scale, move, and rotate our object on the stage.
Making a Sprite
With our shader object, you can pass it however many vertices you want. With a sprite, we want only 4 vertices and two triangles.
To position the sprite, we take the users value (which is in stage space) and convert it to clip space.
To scale the sprite, we similarly take a scale in pixels and convert it into clip space.
Rotation is simple - no modification is needed.
When the sprite is drawn, a transformation matrix with the position, rotation, and scale will be sent to the shader.
Alright, we’ve got our boilerplate code in! Now, let’s take a look at the AGAL shader code.
Writing AGAL Shader Code
Like OpenGL, AGAL shaders are split into vertex and fragment shaders.
Let’s take a look at the vertex shader for this project:
That was a bit anticlimatic! To understand (and write) AGAL code, you need to understand the opcodes which can be found here. Information about registers can be found at the bottom of the page here.
Here’s a breakdown of what’s happening in the shader:
- Vertex attribute 0 (va0, or the vertex position) is being moved into temporary register 0.
- The temporary vertex position is being multiplied by a 4v4 matrix (vc0). vc0 is the transformation matrix we sent to the shader in the
CustomSpritecode. The result of this operation is being sent to the output position, or op.
- Vertex attribute 1 (va1, or the uv) is being moved into a varying register v0.
So even though the syntax looks different, the AGAL shader’s output is pretty much the same as an OpenGL shader. Let’s look at the fragment shader.
Let’s break this shader down as well.
- Sample the texture sampler fs0 (which is the texture we sent in the
ShaderObjectcode) as position v0 (which is our uv), and place the result in temporary register ft0.
- Move the temporary register into the output color.
Ok, so we have our texture function. But what do the macros at the end of the line mean? They are flags to determine how the texture is sampled. These are the options:
- Texture Type
- 2d (for 2d textures)
- cube (for cube maps)
- Pixel Filtering Type
- nearest (for nearest-neighbor filtering)
- linear (bilinear / trilinear filtering)
- mipnone / nomip (no mip-mapping)
- mipnearest (nearing mip-map only)
- miplinear (blend between two nearest mip-maps)
- Texture Repeat Settings
- clamp (clamp texture coordinates to 0-1)
- wrap (wrap texture coordinates by only taking the fractional component)
- repeat (texture coordinates beyond 0-1 will repeat)
The end result should look like this:
All in all, AGAL is just another way of doing the same thing that all shaders do. So once you learn the syntax, it should be smooth sailing!