среда, 18 января 2012 г.

stage3D в ActionScript, история начинается...

Посмотрел немного на шейдеры и "обилие" информации в интернете о реализации эффектов в шейдерах с иcпользованием AGAL (Adobe Graphics Assembly Language) и решил немного поизучать. Создал тестовый образец - вращающийся кубик, на основе которого можно попробовать небольшие эксперименты для себя (чтобы потом не забыть).

Для написания кода ActionScript использую FlashDevelop. Чтобы создать простейшее приложение - вращающийся кубик с разноцветными гранями, необходимо создать новый проект, за основу выбрав AS3 Project. Затем из меню Project выбрать пункт Properties и в списке версии платформы выбрать 11.0 и нажать OK.


Затем в файле Main.as разместить следующее содержимое:


package {
import com.adobe.utils.AGALMiniAssembler;
import com.adobe.utils.PerspectiveMatrix3D;
import flash.display.*;
import flash.display3D.*;
import flash.events.Event;
import flash.events.ErrorEvent;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;

public class Main extends Sprite {
private const VERTEX_SHADER:String =
            "m44 op, va0, vc0 \n" +
            "mov v0, va1";
        private const FRAGMENT_SHADER:String = 
            "mov oc, v0";
private var context3D:Context3D
, indexList: IndexBuffer3D
, vertexes:VertexBuffer3D
, vertexAssembly:AGALMiniAssembler = new AGALMiniAssembler()
, fragmentAssembly:AGALMiniAssembler = new AGALMiniAssembler()
, programPair:Program3D;
private var model:Matrix3D = new Matrix3D()
, view:Matrix3D = new Matrix3D()
, projection:PerspectiveMatrix3D = new PerspectiveMatrix3D()
, finalTransform:Matrix3D = new Matrix3D();
public function Main():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align     = StageAlign.TOP_LEFT;
var stage3D:Stage3D = stage.stage3Ds[0];
stage3D.addEventListener(Event.CONTEXT3D_CREATE, reset);
stage3D.addEventListener(ErrorEvent.ERROR, error);
stage3D.requestContext3D();
stage.addEventListener(Event.RESIZE, resize);
compileShader();
}
private function compileShader():void {
vertexAssembly.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER, false);
fragmentAssembly.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER, false);
}
private function reset(e:Event):void {
context3D = (e.target as Stage3D).context3D;
resize();
setupScene();
}
private function resize(e:Event = null):void {
if (!context3D) return;
context3D.configureBackBuffer(stage.stageWidth - 20, stage.stageHeight - 20, 2, true);
}
private function setupScene():void {
if (!context3D) return;
context3D.enableErrorChecking = true;//if debug
context3D.setCulling(Context3DTriangleFace.BACK);
var triangles:Vector.<uint> = Vector.<uint>([2, 1, 0, 3, 2, 0, 4, 7, 5, 7, 6, 5, 8, 11, 9,  9, 11, 10, 12, 15, 13, 13, 15, 14, 16, 19, 17, 17, 19, 18, 20, 23, 21, 21, 23, 22]);
indexList = context3D.createIndexBuffer(triangles.length);
indexList.uploadFromVector(triangles, 0, triangles.length);
const dataPerVertex:int = 6;
var vertexData:Vector.<Number> = Vector.<Number>([// x,y,z r,g,b
0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1,
1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1]);
vertexes = context3D.createVertexBuffer(vertexData.length/dataPerVertex, dataPerVertex);
vertexes.uploadFromVector(vertexData, 0, vertexData.length/dataPerVertex);
context3D.setVertexBufferAt(0, vertexes, 0, Context3DVertexBufferFormat.FLOAT_3);
context3D.setVertexBufferAt(1, vertexes, 3, Context3DVertexBufferFormat.FLOAT_3);
programPair = context3D.createProgram();
programPair.upload(vertexAssembly.agalcode, fragmentAssembly.agalcode);
context3D.setProgram(programPair);
view.appendTranslation(0, 0, -2);
model.appendTranslation( -.5, -.5, -.5);
projection.perspectiveFieldOfViewRH(45, stage.stageWidth / stage.stageWidth, 1, 1000);
stage.addEventListener(Event.ENTER_FRAME, render);
}
private function render( event:Event ):void {
model.appendRotation(.5, Vector3D.Z_AXIS);
model.appendRotation(.5, Vector3D.Y_AXIS);
model.appendRotation(.5, Vector3D.X_AXIS);
finalTransform.identity();
finalTransform.append(model);
finalTransform.append(view);
finalTransform.append(projection);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform,  true);
context3D.clear(.3, .3, .3);
context3D.drawTriangles(indexList);
context3D.present();
}
private function error(errEv:ErrorEvent):void {
trace(errEv.errorID + ": " + errEv.text);
        }
}
}


Основные функции init - инициализация приложения, compileShader - сборка программ для шейдеров, вершинного и пиксельного (фрагментного по версии Adobe). reset - вызывается при создании контекста 3D. resize - при изменении размеров окна, setupScene - для заполнения индексного и вершинного буферов и для загрузки шейдерной программы и инициализация матриц модели, вида, проекции. Куб занимает пространство от 0 до +1, в матрице модели он смещается на 0.5 (то есть становится от -0.5 до +0.5), матрица вида задается смещением на -2 по координате z. Перспективная проекция строится для угла в 45 градусов, соотношения сторон согласно ширине и высоте

Комментариев нет:

Отправить комментарий