2

我查看了以下网站以获取有关编写 AGAL 以将纹理/位图渲染到Stage3D对象的一些信息:

http://iflash3d.com/shaders/my-name-is-agal-i-come-from-adobe-1/

但它似乎涵盖了更多关于如何在 3D 空间中创建对象的内容。或许无法避免?

但无论如何我会问这个:

编写以将多个精灵渲染Stage3D

注意:不需要过滤器、效果或着色器 - 只需位置、缩放和旋转变换。另外,我不是在寻找现有的在后台执行此操作的 3rd 方 API。我想自己试验这种低级语言。

4

1 回答 1

7

这一切都在您链接的文章中进行了描述,使其成为“2d”的唯一缺少的是创建一个正交投影矩阵。

var projection:Matrix3D = new Matrix3D(Vector.<Number>([
    2 / (viewPort.right - viewPort.left), 0, 0,  0,
    0,  2 / (viewPort.top - viewPort.bottom), 0, 0,
    0,  0, 1 / (far - near), 0,
    0, 0, near / (near - far), 1
]));

一个常见的抽象和优化是精灵批处理。以下示例功能不完整,目前尚未实现颜色色调、排序、旋转和偏移,但这样做很简单。

package
{
    import com.adobe.utils.*;

    import flash.display3D.*;
    import flash.display3D.textures.*;
    import flash.geom.*;
    import flash.system.System;
    import flash.utils.*;

    public class SpriteBatch3D
    {
        private static const SPRITES_PER_BATCH:int = 2048;
        private static const DATA_PER_VERTEX:int = 4;

        private var _context3D:Context3D;

        private var _vertexBuffer:VertexBuffer3D;
        private var _indexBuffer:IndexBuffer3D;
        private var _program:Program3D;
        private var _matrix3D:Matrix3D;
        private var _numSprites:Number;
        private var _sprites:Vector.<SpriteBatch3DSprite>;

        public function SpriteBatch3D(context3D:Context3D)
        { 
            if (context3D == null) {
                throw new ArgumentError("context3D cannot be null.");
            }

            _context3D = context3D;
            allocateBuffers();
            allocateShaders();
            allocateSprites();
        }

        private function allocateSprites():void {
            _sprites = createSprites();
            _numSprites = 0;
        }

        private function createSprites():Vector.<SpriteBatch3DSprite> {
            var sprites:Vector.<SpriteBatch3DSprite> = new Vector.<SpriteBatch3DSprite>(SPRITES_PER_BATCH);

            for (var i:int = 0; i < sprites.length; i++) 
            {
                 sprites[i] = new SpriteBatch3DSprite;
            }

            return sprites;
        }

        private function allocateShaders():void
        {
            var vertexAssembler:AGALMiniAssembler = new AGALMiniAssembler();
            vertexAssembler.assemble(flash.display3D.Context3DProgramType.VERTEX,
                "m44 op, va0, vc0   \n" +
                "mov v0, va1        \n"
            );

            var fragmentAssembler:AGALMiniAssembler = new AGALMiniAssembler();
            fragmentAssembler.assemble(flash.display3D.Context3DProgramType.FRAGMENT, 
                "tex ft1, v0, fs0 <2d,linear> \n" +
                "mov oc, ft1"
            );

            _program = _context3D.createProgram();
            _program.upload(vertexAssembler.agalcode, fragmentAssembler.agalcode);
        }

        private function allocateBuffers():void
        {
            _vertexBuffer = _context3D.createVertexBuffer(SPRITES_PER_BATCH * 4, DATA_PER_VERTEX);
            _vertexBuffer.uploadFromByteArray(createVertexData(), 0, 0, SPRITES_PER_BATCH * 4);

            _indexBuffer = _context3D.createIndexBuffer(SPRITES_PER_BATCH * 6);
            _indexBuffer.uploadFromByteArray(createIndexData(), 0, 0, SPRITES_PER_BATCH * 6);
        }

        private function createVertexData():ByteArray
        {
            var data:ByteArray = new ByteArray;
            data.length = SPRITES_PER_BATCH * 4 * (4 * DATA_PER_VERTEX);
            data.endian = Endian.LITTLE_ENDIAN;

            return data;

        }
        private function createIndexData():ByteArray {

            var data:ByteArray = new ByteArray();
            data.endian = Endian.LITTLE_ENDIAN;
            data.length = (SPRITES_PER_BATCH * 6) * 2;

            for (var i:int = 0; i < SPRITES_PER_BATCH * 6; i++)
            {
                data.writeShort(i * 4);
                data.writeShort(i * 4 + 1);
                data.writeShort(i * 4 + 2);

                data.writeShort(i * 4);
                data.writeShort(i * 4 + 2);
                data.writeShort(i * 4 + 3);
            }

            return data;
        }

        public function dispose():void {
            if (_vertexBuffer) {
                _vertexBuffer.dispose();
                _vertexBuffer = null;
            }

            if (_indexBuffer) {
                _indexBuffer.dispose();
                _indexBuffer = null;
            }
        }

        public function begin(matrix3D:Matrix3D):void {
            _matrix3D = matrix3D;
        }

        public function draw(texture:Texture, destination:Rectangle,  source:Rectangle=null, origin:Point=null, color:int=0xFFFFFF, rotation:Number=0, depth:Number=0):void {
            if (source == null) {
                source = new Rectangle(0, 0, 1, 1);
            }

            if (origin == null) {
                origin = new Point(0, 0);
            }

            if (_sprites.length == _numSprites) {
                _sprites = _sprites.concat(createSprites());                
            }

            _sprites[_numSprites].texture       = texture;
            _sprites[_numSprites].destination   = destination;
            _sprites[_numSprites].source        = source;
            _sprites[_numSprites].origin        = origin;
            _sprites[_numSprites].rotation      = rotation;
            _sprites[_numSprites].depth         = depth;

            _numSprites++;          
        }

        public function end():void {    
            _context3D.setProgram(_program);
            _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _matrix3D, true);

            _context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2); // x, y
            _context3D.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2); // u, v

            _context3D.setCulling(Context3DTriangleFace.FRONT);
            _context3D.setDepthTest(false, Context3DCompareMode.ALWAYS);

            // TODO sorting.
            var sprites:Vector.<SpriteBatch3DSprite> = _sprites;
            var texture:Texture = null;
            var firstIndex:int = 0;

            for (var index:int = 0; index < _numSprites; index++) {
                if (texture != sprites[index].texture) {
                    if (index > firstIndex) {
                        renderBatch(texture, sprites, firstIndex, index - firstIndex);
                        firstIndex = index;
                    }

                    texture = sprites[index].texture;
                }
            }

            renderBatch(texture, sprites, firstIndex, _numSprites - firstIndex);
            _numSprites = 0;
        }

        private function renderBatch(texture:Texture, sprites:Vector.<SpriteBatch3DSprite>, offset:int, count:int):void {
            if (_numSprites == 0) {
                return;
            }

            while(count > 0) {
                var data:ByteArray = new ByteArray;
                data.endian = Endian.LITTLE_ENDIAN;

                var size:int = count;
                if (size > SPRITES_PER_BATCH) {
                    size = Math.min(sprites.length, SPRITES_PER_BATCH); 
                }

                for (var i:int = offset; i < (offset + size); i++) {
                    data.writeFloat(sprites[i].destination.left);
                    data.writeFloat(sprites[i].destination.top);
                    data.writeFloat(sprites[i].source.left);
                    data.writeFloat(sprites[i].source.top);

                    data.writeFloat(sprites[i].destination.left);
                    data.writeFloat(sprites[i].destination.bottom);
                    data.writeFloat(sprites[i].source.left);
                    data.writeFloat(sprites[i].source.bottom);

                    data.writeFloat(sprites[i].destination.right);
                    data.writeFloat(sprites[i].destination.bottom);
                    data.writeFloat(sprites[i].source.right);
                    data.writeFloat(sprites[i].source.bottom);

                    data.writeFloat(sprites[i].destination.right);
                    data.writeFloat(sprites[i].destination.top);
                    data.writeFloat(sprites[i].source.right);
                    data.writeFloat(sprites[i].source.top);
                }

                _vertexBuffer.uploadFromByteArray(data, 0, 0, size * 4);            
                _context3D.setTextureAt(0, texture);
                _context3D.drawTriangles(_indexBuffer, 0, size * 2);

                offset += size;
                count -= size;
            }
        }
    }
}

import flash.display3D.textures.*;
import flash.geom.*;

internal class SpriteBatch3DSprite {
    public var texture:Texture;
    public var source:Rectangle;
    public var destination:Rectangle;
    public var origin:Point;
    public var rotation:Number;
    public var depth:Number;
}

用法

batch.begin(matrix);
batch.draw(texture1, new Rectangle(100, 0, 100, 100));
batch.draw(texture2, new Rectangle(0, 200, 100, 100));
batch.draw(texture3, new Rectangle(0, 500, 100, 100));
batch.draw(texture4, new Rectangle(900, 0, 100, 100));
batch.end();

然而,有一些琐碎的警告,因为纹理对象不携带宽度或高度吸气剂,您正在处理纹素空间 (0...1) 中的源矩形

此外,Context3D 不携带任何有关视口的信息,因此无法在内部构造矩阵,必须将其解析为依赖项。

于 2011-11-30T23:12:14.847 回答