• Stage3D学习笔记(四):正交矩阵


    我们上一章节显示图片的时候,会发现我们制定的顶点在Stage3D中其实是存在一个区间的:

    x轴(从左到右):[-1.0-1.0]

    y轴(从下到上):[-1.0-1.0]

    z轴(从近到远):[0-1.0]

    超过这个区间的部分我们的图片都会看不见,大家可以重新修改上一节的代码中的顶点位置查看;

    并且该区间不会跟随Stage3D的尺寸改变而改变,即无论Stage3D的显示尺寸如何变动,绘制的图像是会进行对应的拉伸操作的;

    那么这就导致了一个问题的出现,如果我需要显示图片的原有尺寸(或者指定的尺寸)且该尺寸不会跟随Stage3D的显示尺寸变动时该怎么办?

    为了解决这个问题,我们需要引入一个叫做矩阵转换的概念,即通过一定的转换运算把我们设定的尺寸转换为适用于Stage3D中的坐标尺寸。

    一般存在两种矩阵转换:正交矩阵和透视矩阵。

    透视矩阵:遵循现实世界中的近大远小法则,一般的3D游戏都使用该矩阵。

    正交矩阵:没有近大远小的规则,所有物体看上去都是一样的大小,一般通过3D来实现的2D游戏都使用该矩阵。

    我们基于上一章的代码,先修改一下要显示的纹理的顶点:

     1 private function initBuffer():void
     2 {
     3     //顶点数据
     4     var vertexData:Vector.<Number> = Vector.<Number>(
     5             [
     6             //  x,  y,  z, u, v
     7                 -1, -1, 0, 0, 1,
     8                 1,  -1, 0, 1, 1,
     9                 1,   1, 0, 1, 0,
    10                 -1,  1, 0, 0, 0
    11             ]);
    12 //省略
    13 }

    看一下效果:

    我们看见,整个纹理都铺满了3D场景。同时纹理的原有的高宽比没有了。

    使用正交矩阵:

    首先需要创建一个矩阵对象保存正交矩阵:

    1 //存放正交矩阵的对象
    2 private var _projectionMatrix:Matrix3D;

    创建正交矩阵:

     1 /**
     2  * 创建正交矩阵.
     3  * @param width 场景宽度.
     4  * @param height 场景高度.
     5  * @param near 近截面.
     6  * @param far 远截面.
     7  */
     8 private function initOrthographicProjection(Number, height:Number, near:Number = -1.0, far:Number = 1.0):void
     9 {
    10     //创建正交矩阵的实例
    11     _projectionMatrix = new Matrix3D();
    12     //获取场景宽度和高度的比例
    13     var ratio:Number = width / height;
    14     //获取正交矩阵数据
    15     var coords:Vector.<Number> = getOrthographicMatrix(-ratio, ratio, -1, 1, near, far);
    16     //拷贝数据到矩阵中
    17     _projectionMatrix.copyRawDataFrom(coords);
    18     //将我们的正交矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
    19     _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _projectionMatrix, true);
    20 }
    21 
    22 /**
    23  * 获取正交矩阵的数据.
    24  * @param left 左边界.
    25  * @param right 右边界.
    26  * @param bottom 底边界.
    27  * @param top 顶边界.
    28  * @param near 近截面.
    29  * @param far 远截面.
    30  * @return 正交矩阵的数据.
    31  */
    32 private function getOrthographicMatrix(left:Number, right:Number, bottom:Number, top:Number, near:Number, far:Number):Vector.<Number>
    33 {
    34     var m:Vector.<Number> = new Vector.<Number>(16, true);
    35     
    36     m[0] = 2.0 * 1.0 / (right - left);
    37     m[5] = 2.0 * 1.0 / (top - bottom);
    38     m[10] = 1.0 / (far - near);
    39     
    40     m[12] = (right + left) / (right - left);
    41     m[13] = (bottom + top) / (bottom - top);
    42     m[14] = near / (near - far);
    43     
    44     m[1] = m[2] = m[3] = m[4] =m[6] = m[7] = m[8] = m[9] = m[11] = 0;
    45     m[15] = 1.0;
    46     
    47     return m;
    48 }

    正交矩阵的信息需要作为常量传递到GPU中:

    1 //将我们的正交矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
    2 _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _projectionMatrix, true);

    最后,修改一下顶点着色器,使我们的顶点数据和正交矩阵相乘:

     1 private function initProgram():void
     2 {
     3     //顶点着色器代码, 每个上传的顶点前都会执行一次该代码
     4     var vertexArr:Array =
     5             [
     6                 //op 代表位置输出寄存器, 无论对顶点进行多少次的运算最终都要将结果
     7                 //赋值给他, 这里和我们的正交矩阵进行相乘的运算
     8                 "m44 op, va0, vc0",
     9                 //片段着色器需要用的数据要在这里通过 v0 中转一下, 因为片段着色器不
    10                 //能直接读取 va0 和 va1 的数据
    11                 "mov v0, va1"
    12             ];
    13 //省略
    14 }

    改好了以后,看一下现在的效果:

    我们发现纹理的高宽比被保留了下来,说明我们的正交矩阵开始起作用了!

    保留我们的纹理的尺寸只要修改顶点的数据即可:

     1 private function initBuffer():void
     2 {
     3     //顶点数据
     4     var vertexData:Vector.<Number> = Vector.<Number>(
     5             [
     6             //  x,                y,                z, u, v
     7                 -1 * (128 / 500), -1 * (128 / 500), 0, 0, 1,
     8                 1 * (128 / 500),  -1 * (128 / 500), 0, 1, 1,
     9                 1 * (128 / 500),   1 * (128 / 500), 0, 1, 0,
    10                 -1 * (128 / 500),  1 * (128 / 500), 0, 0, 0
    11             ]);
    12 //省略
    13 }

    顶点x和y都乘上宽度与高度分别除以场景高度的系数即可。

    最终效果:

    上代码:

      1 package
      2 {
      3     import com.adobe.utils.AGALMiniAssembler;
      4     
      5     import flash.display.Bitmap;
      6     
      7     import flash.display.Sprite;
      8     import flash.display.Stage3D;
      9     import flash.display3D.Context3D;
     10     import flash.display3D.Context3DProfile;
     11     import flash.display3D.Context3DProgramType;
     12     import flash.display3D.Context3DRenderMode;
     13     import flash.display3D.Context3DTextureFormat;
     14     import flash.display3D.Context3DVertexBufferFormat;
     15     import flash.display3D.IndexBuffer3D;
     16     import flash.display3D.Program3D;
     17     import flash.display3D.VertexBuffer3D;
     18     import flash.display3D.textures.Texture;
     19     import flash.events.ErrorEvent;
     20     import flash.events.Event;
     21     import flash.geom.Matrix3D;
     22     
     23     [SWF(width=800, height=600, frameRate=60)]
     24     public class OrthographicProjection extends Sprite
     25     {
     26         [Embed(source="img.png")]
     27         private var IMG_CLASS:Class;
     28         
     29         //3D 场景对象
     30         private var _stage3D:Stage3D;
     31         //3D 上下文渲染对象
     32         private var _context3D:Context3D;
     33         
     34         //顶点缓冲数据
     35         private var _vertexBuffer:VertexBuffer3D;
     36         //索引缓冲数据
     37         private var _indexBuffer:IndexBuffer3D;
     38         //纹理数据对象
     39         private var _texture:Texture;
     40         
     41         //着色器对象
     42         private var _program3D:Program3D;
     43         
     44         //存放正交矩阵的对象
     45         private var _projectionMatrix:Matrix3D;
     46         
     47         public function OrthographicProjection()
     48         {
     49             addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
     50         }
     51         
     52         private function addedToStageHandler(event:Event):void
     53         {
     54             removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
     55             
     56             //3D 场景存在, 一般存在 4 个 3D 场景对象
     57             if(stage.stage3Ds.length > 0)
     58             {
     59                 //使用最下层的 3D 场景
     60                 _stage3D = stage.stage3Ds[0];
     61                 //请求 3D 上下文渲染对象
     62                 _stage3D.addEventListener(ErrorEvent.ERROR, stage3DErrorHandler);
     63                 _stage3D.addEventListener(Event.CONTEXT3D_CREATE, context3DCreateHandler);
     64                 _stage3D.requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.BASELINE);
     65             }
     66         }
     67         
     68         private function stage3DErrorHandler(event:ErrorEvent):void
     69         {
     70             trace("Context3D对象请求失败:", event.text);
     71         }
     72         
     73         private function context3DCreateHandler(event:Event):void
     74         {
     75             initContext3D(700, 500);
     76             initOrthographicProjection(700, 500);
     77             initBuffer();
     78             initTexture();
     79             initProgram();
     80             
     81             //每帧进行渲染
     82             addEventListener(Event.ENTER_FRAME, render);
     83         }
     84         
     85         private function initContext3D(Number, height:Number):void
     86         {
     87             //获取 3D 渲染对象
     88             _context3D = _stage3D.context3D;
     89             //调整 3D 舞台位置
     90             _stage3D.x = 50;
     91             _stage3D.y = 50;
     92             //设置后台缓冲区
     93             _context3D.configureBackBuffer(width, height, 2);
     94         }
     95         
     96         /**
     97          * 创建正交矩阵.
     98          * @param width 场景宽度.
     99          * @param height 场景高度.
    100          * @param near 近截面.
    101          * @param far 远截面.
    102          */
    103         private function initOrthographicProjection(Number, height:Number, near:Number = -1.0, far:Number = 1.0):void
    104         {
    105             //创建正交矩阵的实例
    106             _projectionMatrix = new Matrix3D();
    107             //获取场景宽度和高度的比例
    108             var ratio:Number = width / height;
    109             //获取正交矩阵数据
    110             var coords:Vector.<Number> = getOrthographicMatrix(-ratio, ratio, -1, 1, near, far);
    111             //拷贝数据到矩阵中
    112             _projectionMatrix.copyRawDataFrom(coords);
    113             //将我们的正交矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
    114             _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _projectionMatrix, true);
    115         }
    116         
    117         /**
    118          * 获取正交矩阵的数据.
    119          * @param left 左边界.
    120          * @param right 右边界.
    121          * @param bottom 底边界.
    122          * @param top 顶边界.
    123          * @param near 近截面.
    124          * @param far 远截面.
    125          * @return 正交矩阵的数据.
    126          */
    127         private function getOrthographicMatrix(left:Number, right:Number, bottom:Number, top:Number, near:Number, far:Number):Vector.<Number>
    128         {
    129             var m:Vector.<Number> = new Vector.<Number>(16, true);
    130             
    131             m[0] = 2.0 * 1.0 / (right - left);
    132             m[5] = 2.0 * 1.0 / (top - bottom);
    133             m[10] = 1.0 / (far - near);
    134             
    135             m[12] = (right + left) / (right - left);
    136             m[13] = (bottom + top) / (bottom - top);
    137             m[14] = near / (near - far);
    138             
    139             m[1] = m[2] = m[3] = m[4] =m[6] = m[7] = m[8] = m[9] = m[11] = 0;
    140             m[15] = 1.0;
    141             
    142             return m;
    143         }
    144         
    145         private function initBuffer():void
    146         {
    147             //顶点数据
    148             var vertexData:Vector.<Number> = Vector.<Number>(
    149                     [
    150                     //  x,                y,                z, u, v
    151                         -1 * (128 / 500), -1 * (128 / 500), 0, 0, 1,
    152                         1 * (128 / 500),  -1 * (128 / 500), 0, 1, 1,
    153                         1 * (128 / 500),   1 * (128 / 500), 0, 1, 0,
    154                         -1 * (128 / 500),  1 * (128 / 500), 0, 0, 0
    155                     ]);
    156             
    157             //创建顶点缓冲对象, 参数设定存在几组数据和每组数据的个数
    158             _vertexBuffer = _context3D.createVertexBuffer(vertexData.length / 5, 5);
    159             //上传顶点数据到GPU, 参数设定从第几组数据开始上传和上传多少组数据
    160             _vertexBuffer.uploadFromVector(vertexData, 0, vertexData.length / 5);
    161             
    162             //索引数据
    163             var indexData:Vector.<uint> = Vector.<uint>(
    164                     [
    165                         0, 3, 1,
    166                         1, 2, 3
    167                     ]);
    168             
    169             //创建索引缓冲对象, 每个索引对应顶点数据中的相对应的一组数据, 
    170             //每3个索引组成一个会被绘制出来的三角形, 参数指定索引的长度
    171             _indexBuffer = _context3D.createIndexBuffer(indexData.length);
    172             //上传索引数据到GPU, 参数设定从第几个数据开始上传和上传多少个数据
    173             _indexBuffer.uploadFromVector(indexData, 0, indexData.length);
    174         }
    175         
    176         private function initTexture():void
    177         {
    178             //创建位图
    179             var bitmap:Bitmap = new IMG_CLASS() as Bitmap;
    180             //创建纹理, 注意尺寸必须是 2 的幂数
    181             _texture = _context3D.createTexture(128, 128, Context3DTextureFormat.BGRA, true);
    182             //上传纹理到 GPU, 第二个参数表示该纹理的 mipmap 级别, 级别零是高级全分辨率图像
    183             _texture.uploadFromBitmapData(bitmap.bitmapData, 0);
    184         }
    185         
    186         private function initProgram():void
    187         {
    188             //顶点着色器代码, 每个上传的顶点前都会执行一次该代码
    189             var vertexArr:Array =
    190                     [
    191                         //op 代表位置输出寄存器, 无论对顶点进行多少次的运算最终都要将结果
    192                         //赋值给他, 这里和我们的正交矩阵进行相乘的运算
    193                         "m44 op, va0, vc0",
    194                         //片段着色器需要用的数据要在这里通过 v0 中转一下, 因为片段着色器不
    195                         //能直接读取 va0 和 va1 的数据
    196                         "mov v0, va1"
    197                     ];
    198             
    199             //片段着色器代码, 每个可以显示的像素都会执行一次该代码
    200             var fragmentArr:Array =
    201                     [
    202                         //对纹理 fs0 进行取样, 通过 v0 代表的 uv 坐标来获取对应的像素点颜
    203                         //色, 将该颜色值存储到 ft0 中
    204                         "tex ft0, v0, fs0 <2d,repeat,linear,nomip>",
    205                         //oc 代表颜色输出寄存器, 每个顶点的颜色数据都要赋值给他
    206                         "mov oc, ft0"
    207                     ];
    208             
    209             //使用 Adobe 自己提供的编译器编译代码为程序可使用的二进制数据
    210             var assembler:AGALMiniAssembler = new AGALMiniAssembler();
    211             _program3D = assembler.assemble2(_context3D, 1, vertexArr.join("
    "), fragmentArr.join("
    "));
    212             
    213             //----- 这段代码是从 render 里搬过来的, 因为不会进行改动就不放在帧循环中了 -----
    214             
    215             //指定着色器代码的 va0 代表的数据段, 表示顶点的 x, y, z 坐标
    216             _context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
    217             //指定着色器代码的 va1 代表的数据段, 表示顶点的 u, v 数据
    218             _context3D.setVertexBufferAt(1, _vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
    219             //指定上传的纹理由 fs0 表示
    220             _context3D.setTextureAt(0, _texture);
    221             //指定当前使用的着色器对象
    222             _context3D.setProgram(_program3D);
    223         }
    224         
    225         private function render(event:Event):void
    226         {
    227             //清除已绘制过的 3D 图像
    228             _context3D.clear();
    229             //通过顶点索引数据绘制所有的三角形
    230             _context3D.drawTriangles(_indexBuffer);
    231             //将后台缓冲的图像显示到屏幕
    232             _context3D.present();
    233         }
    234     }
    235 }
  • 相关阅读:
    UVALive 7276 Wooden Signs
    hdu4291 A Short problem
    A
    hdu4686 Arc of Dream
    thinkphp5 模型的 更新操作
    thinkphp5 模型的 新增操作
    thinkphp 模型的创建
    thinkphp5 增删改查操作
    tp5 的查询构造器
    thinkphp5 数据库的原生查询
  • 原文地址:https://www.cnblogs.com/hammerc/p/4070860.html
Copyright © 2020-2023  润新知