This website uses cookies to improve user experience. By using our website you consent to all cookies in accordance with our Cookie Policy. X

Basics of Stage3D part IV: working with shaders [expert]

Nowadays shaders have become an inharent part of the 3D graphics, it is basically impossible to display a picture without creating a single shading program. Which is why, this time around we will take a quick look on how to work with shaders by adding a simple lighting to our virtual world. In Flash shaders are created using a series of instructions called AGAL, which is a very different language from ActionSctipt that requires a complete walkthrough on its own for full understanding - those however are easy to find on the Internet so for now we will only focus on the most basic stuff needed for lighting to work.
Returning to the code from previous article first thing we should do is add new Fragment Shader:
private const FRAGMENT_SHADER_LIGHT:String = "dp3 ft1, fc2, v1 n"+
"neg ft1, ft1 n"+
"max ft1, ft1, fc0 n"+
"mul ft2, fc4, ft1 n"+
"mul ft2, ft2, fc3 n"+
"add oc, ft2, fc1";
and Vertex Shader:
private const VERTEX_SHADER_LIGHT:String = "mov vt0, va0n"+
"m44 op, vt0, vc0n"+
"nrm vt1.xyz, va0.xyzn"+
"mov vt1.w, va0.wn"+	
"mov v1, vt1n" +
"mov v2, va1";
(The code has been borrowed from http://blog.norbz.net/2012/04/stage3d-agal-from-scratch-part-vii-let-there-be-light)
Above shaders will render triangles by taking each pixel and applying a light or dark color depending on their relative position to the camera. Before we do anything with that code we still need to compile it into a special "program" using the AGALMiniAssembler:
vertexAssembly.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER_LIGHT, false);
fragmentAssembly.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER_LIGHT, false);

programPairLight = renderContext.createProgram();
programPairLight.upload(vertexAssembly.agalcode, fragmentAssembly.agalcode);
(Don't forget to prepare the programPairLight variable)
So, right now we have two shaders: the standardprogramPair and light generating programPairLight. Only thing left to do is to decide which one we will be using by calling the setProgram function in Context3D. To better showcase the difference lets create another Box3D (lets call it "box3DLight") and then add the following code before present() but after the "box3D" rendering:
boxMat = box3DLight.getMatrix3D();
boxMat.identity();
boxMat.append(rotMat);
boxMat.appendTranslation(posVec.x + 1, posVec.y + 1, posVec.z+1);

finalTransform.identity();
finalTransform.append(boxMat);
finalTransform.append(world);
finalTransform.append(camera);
finalTransform.append(projection);
	
renderContext.setProgram(programPairLight);
renderContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform, true);

var p:Vector3D = camera.position.clone();
p.normalize();
var m3d:Matrix3D = finalTransform.clone();
m3d.invert();
p = m3d.transformVector(p);
p.normalize();
p.negate();

renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([0,0,0,0]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, Vector.<Number>([0,0,0,0]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, Vector.<Number>([p.x,p.y,p.z,1]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 3, Vector.<Number>([1, 1, 1, 1]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 4, Vector.<Number>([0.6, 0.6, 0.6, 1]) );

box3DLight.render(renderContext);
That is a lot to take in, but what really matters to use happens after setProgram ("boxMat" and "finalTransform" operations are previously discussed transformations from 3D to 2D). The term "program" is not used here without a reason as just like any other program on our computer shaders do a mathematical operations on variables and constants, only by using a very low-leveled language which is also quite limited as it only support up to 8 variables at a time and they only in program's memory. That is not the case for constants, although they are still limited to a maximum of 8, they can be supplied from the outside using either setProgramConstantsFromVector or setProgramConstantsFromMatrix functions - precisely what the code above is doing, it supplies the camera position ("p" variable) and light's color.
When done, we should see something like this:
This application only uses two shaders but there is nothing stopping us from create much, much and then just switch them around using the setProgram function just as we are doing with the lineStyle function when working with Graphics.
Whole code is available here: Tutorial3D_light.zip.

Name:
Comment:
Confirm the image code:confirm image

 
Chady K writes:
06.07.2013 15:44
Thanks for the series :)