在2D空间中将纹理(BitmapData)渲染到Stage3D所需的低级代码是什么?

big*_*igp 2 flash actionscript-3 low-level molehill stage3d

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

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

但它似乎更多地涉及如何在3D空间中创建对象.也许这是无法避免的?

但无论如何我会问这个问题:

什么是裸骨必需的AGAL代码和AS3代码写入以呈现多个精灵Stage3D

注意:不需要滤镜,效果或着色器 - 只需位置,缩放和旋转变换.此外,我不是在寻找现有的第三方API.我想自己试一下这种低级语言.

Cas*_*yer 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
]));
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

用法

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();
Run Code Online (Sandbox Code Playgroud)

然而,有一些琐碎的警告,因为纹理对象不携带宽度或高度的getter,所以你在texel空间中处理源矩形(0 ... 1)

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