zoukankan      html  css  js  c++  java
  • Stage3D学习笔记(五):通过矩阵操作纹理

    虽然我们上一节已经实现了正交矩阵的显示,但是可以明显的感觉到要调整显示纹理的坐标和尺寸是相当复杂的,需要对每个顶点进行操作,如果还要加上注册点和旋转的话,用上一节的方法来做是会让人发疯的!

    所以我们距离实用还有很长的路要走,加上之前面试时由于对Starling内部实现的不了解导致的失利,所以接下来的一段学习笔记会专注在通过正交矩阵来实现2D框架的实现上,并参考Starling的架构做一个精简的山寨版!

    首要任务,就是要能方便的对纹理设置位置、尺寸和旋转!下面介绍Starling中使用的方法,借助Flash原生的Matrix3D类来实现对纹理的操作。

    我们基于Stage3D学习笔记(三)的代码来修改;

    首先我们除了需要一个正交矩阵用来进行正交运算外,还需要一个矩阵用来记录模型的信息(我们可以把显示的纹理看做由两个三角面拼成的一个四方形模型)。

    1 //正交矩阵
    2 private var _projectionMatrix:Matrix3D;
    3 //模型矩阵, 通过操作该矩阵来变换纹理的显示
    4 private var _modelViewMatrix:Matrix3D;

    正交矩阵的初始化相对于上一节有所变动,为了清晰没有使用新版Starling的简化过的矩阵,而是使用第一版的矩阵。

     1 private function initOrthographicProjection(Number, height:Number, near:Number = -1.0, far:Number = 1.0):void
     2 {
     3     //创建正交矩阵的实例
     4     _projectionMatrix = new Matrix3D();
     5     //设置正交矩阵数据, 这个公式记死即可
     6     var coords:Vector.<Number> = new <Number>
     7             [
     8                 2.0 / width, 0.0, 0.0, 0.0,
     9                 0.0, -2.0 / height, 0.0, 0.0,
    10                 0.0, 0.0, -2.0 / (far - near), 0.0,
    11                 -1.0, 1.0, -(far + near) / (far - near), 1.0
    12             ];
    13     _projectionMatrix.copyRawDataFrom(coords);
    14 }

    还有一点需要更改的是,由于我们的模型的尺寸选择等信息都交由_modelViewMatrix对象来存储,所以顶点数据可以不需要修改,因为我们的需求只有一个,就是显示一个四边形的图片,所以顶点数据可以写死,可以去掉z轴信息,uv信息和xy的信息一致,也可以去掉,不过我们这里进行了保留。

     1 private function initBuffer():void
     2 {
     3     //顶点数据, 因为只需要显示一张图片所以这里的顶点数据是可以写死的, 同时可以去掉 z 轴
     4     //的数据, 因为不需要使用到 z 轴, 我们按照下面的规则来排列顶点:
     5     //0 - 1
     6     //| / |
     7     //2 - 3
     8     //有趣的是 uv 的数据和顶点数据其实是一致的, 所以 uv 的数据也可以去除, 不过我们这里
     9     //先留着, Starling 框架中 uv 数据是已经去掉的
    10     var vertexData:Vector.<Number> = Vector.<Number>(
    11             [
    12             //  x, y, u, v
    13                 0, 0, 0, 0,
    14                 1, 0, 1, 0,
    15                 0, 1, 0, 1,
    16                 1, 1, 1, 1
    17             ]);
    18 //省略
    19 }

    由于去掉了z轴的数据,所以数据上传的格式也要进行相应的更改,具体可以看最终的代码。

    模型矩阵是直接操作当前的纹理的显示的,对他的修改也比较简单:

     1 private function transformMatrix():void
     2 {
     3     //设置纹理的转换矩阵
     4     _modelViewMatrix = new Matrix3D();
     5     //设置矩阵的位置
     6     _modelViewMatrix.prependTranslation(0, 0, 0);
     7     //设置矩阵的旋转
     8     _modelViewMatrix.prependRotation(0, Vector3D.Z_AXIS);
     9     //设置矩阵的尺寸
    10     _modelViewMatrix.prependScale(128, 128, 1);
    11     //设置纹理的中心点
    12     _modelViewMatrix.prependTranslation(0, 0, 0);
    13     
    14     //将纹理的转换矩阵和正交矩阵结合就得到了最终需要的上传到 GPU 进行运算的矩阵数据
    15     var mvpMatrix:Matrix3D = new Matrix3D();
    16     mvpMatrix.append(_modelViewMatrix);
    17     mvpMatrix.append(_projectionMatrix);
    18     //将我们的最终矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
    19     _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mvpMatrix, true);
    20 }

    需要注意的是,模型矩阵和正交矩阵是需要进行合并的。

    我们看一下现在的效果:

    不错哦,是我们想要的效果,下面测试一下旋转和位移:

     1 private function transformMatrix():void
     2 {
     3     //设置纹理的转换矩阵
     4     _modelViewMatrix = new Matrix3D();
     5     //设置矩阵的位置
     6     _modelViewMatrix.prependTranslation(100, 100, 0);
     7     //设置矩阵的旋转
     8     _modelViewMatrix.prependRotation(45, Vector3D.Z_AXIS);
     9     //设置矩阵的尺寸
    10     _modelViewMatrix.prependScale(128, 128, 1);
    11     //设置纹理的中心点
    12     _modelViewMatrix.prependTranslation(0, 0, 0);
    13 //省略
    14 }

    直接给出代码了:

      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     import flash.geom.Vector3D;
     23     
     24     [SWF(width=550, height=400, frameRate=60)]
     25     public class Matrix3DTest extends Sprite
     26     {
     27         [Embed(source="img.png")]
     28         private var IMG_CLASS:Class;
     29         
     30         //3D 场景对象
     31         private var _stage3D:Stage3D;
     32         //3D 上下文渲染对象
     33         private var _context3D:Context3D;
     34         
     35         //顶点缓冲数据
     36         private var _vertexBuffer:VertexBuffer3D;
     37         //索引缓冲数据
     38         private var _indexBuffer:IndexBuffer3D;
     39         //纹理数据对象
     40         private var _texture:Texture;
     41         
     42         //着色器对象
     43         private var _program3D:Program3D;
     44         
     45         //正交矩阵
     46         private var _projectionMatrix:Matrix3D;
     47         //模型矩阵, 通过操作该矩阵来变换纹理的显示
     48         private var _modelViewMatrix:Matrix3D;
     49         
     50         public function Matrix3DTest()
     51         {
     52             addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
     53         }
     54         
     55         private function addedToStageHandler(event:Event):void
     56         {
     57             removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
     58             
     59             //3D 场景存在, 一般存在 4 个 3D 场景对象
     60             if(stage.stage3Ds.length > 0)
     61             {
     62                 //使用最下层的 3D 场景
     63                 _stage3D = stage.stage3Ds[0];
     64                 //请求 3D 上下文渲染对象
     65                 _stage3D.addEventListener(ErrorEvent.ERROR, stage3DErrorHandler);
     66                 _stage3D.addEventListener(Event.CONTEXT3D_CREATE, context3DCreateHandler);
     67                 _stage3D.requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.BASELINE);
     68             }
     69         }
     70         
     71         private function stage3DErrorHandler(event:ErrorEvent):void
     72         {
     73             trace("Context3D对象请求失败:", event.text);
     74         }
     75         
     76         private function context3DCreateHandler(event:Event):void
     77         {
     78             initContext3D();
     79             initOrthographicProjection(stage.stageWidth, stage.stageHeight);
     80             initBuffer();
     81             initTexture();
     82             transformMatrix();
     83             initProgram();
     84             
     85             //每帧进行渲染
     86             addEventListener(Event.ENTER_FRAME, render);
     87         }
     88         
     89         private function initContext3D():void
     90         {
     91             //获取 3D 渲染对象
     92             _context3D = _stage3D.context3D;
     93             //设置后台缓冲区
     94             _context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2);
     95         }
     96         
     97         private function initOrthographicProjection(Number, height:Number, near:Number = -1.0, far:Number = 1.0):void
     98         {
     99             //创建正交矩阵的实例
    100             _projectionMatrix = new Matrix3D();
    101             //设置正交矩阵数据, 这个公式记死即可
    102             var coords:Vector.<Number> = new <Number>
    103                     [
    104                         2.0 / width, 0.0, 0.0, 0.0,
    105                         0.0, -2.0 / height, 0.0, 0.0,
    106                         0.0, 0.0, -2.0 / (far - near), 0.0,
    107                         -1.0, 1.0, -(far + near) / (far - near), 1.0
    108                     ];
    109             _projectionMatrix.copyRawDataFrom(coords);
    110         }
    111         
    112         private function initBuffer():void
    113         {
    114             //顶点数据, 因为只需要显示一张图片所以这里的顶点数据是可以写死的, 同时可以去掉 z 轴
    115             //的数据, 因为不需要使用到 z 轴, 我们按照下面的规则来排列顶点:
    116             //0 - 1
    117             //| / |
    118             //2 - 3
    119             //有趣的是 uv 的数据和顶点数据其实是一致的, 所以 uv 的数据也可以去除, 不过我们这里
    120             //先留着, Starling 框架中 uv 数据是已经去掉的
    121             var vertexData:Vector.<Number> = Vector.<Number>(
    122                     [
    123                     //  x, y, u, v
    124                         0, 0, 0, 0,
    125                         1, 0, 1, 0,
    126                         0, 1, 0, 1,
    127                         1, 1, 1, 1
    128                     ]);
    129             
    130             //创建顶点缓冲对象, 参数设定存在几组数据和每组数据的个数
    131             _vertexBuffer = _context3D.createVertexBuffer(vertexData.length / 4, 4);
    132             //上传顶点数据到GPU, 参数设定从第几组数据开始上传和上传多少组数据
    133             _vertexBuffer.uploadFromVector(vertexData, 0, vertexData.length / 4);
    134             
    135             //索引数据
    136             var indexData:Vector.<uint> = Vector.<uint>(
    137                     [
    138                         0, 1, 2,
    139                         1, 2, 3
    140                     ]);
    141             
    142             //创建索引缓冲对象, 每个索引对应顶点数据中的相对应的一组数据, 
    143             //每3个索引组成一个会被绘制出来的三角形, 参数指定索引的长度
    144             _indexBuffer = _context3D.createIndexBuffer(indexData.length);
    145             //上传索引数据到GPU, 参数设定从第几个数据开始上传和上传多少个数据
    146             _indexBuffer.uploadFromVector(indexData, 0, indexData.length);
    147         }
    148         
    149         private function initTexture():void
    150         {
    151             //创建位图
    152             var bitmap:Bitmap = new IMG_CLASS() as Bitmap;
    153             //创建纹理, 注意尺寸必须是 2 的幂数
    154             _texture = _context3D.createTexture(128, 128, Context3DTextureFormat.BGRA, true);
    155             //上传纹理到 GPU, 第二个参数表示该纹理的 mipmap 级别, 级别零是高级全分辨率图像
    156             _texture.uploadFromBitmapData(bitmap.bitmapData, 0);
    157         }
    158         
    159         private function transformMatrix():void
    160         {
    161             //设置纹理的转换矩阵
    162             _modelViewMatrix = new Matrix3D();
    163             //设置矩阵的位置
    164             _modelViewMatrix.prependTranslation(100, 100, 0);
    165             //设置矩阵的旋转
    166             _modelViewMatrix.prependRotation(45, Vector3D.Z_AXIS);
    167             //设置矩阵的尺寸
    168             _modelViewMatrix.prependScale(128, 128, 1);
    169             //设置纹理的中心点
    170             _modelViewMatrix.prependTranslation(0, 0, 0);
    171             
    172             //将纹理的转换矩阵和正交矩阵结合就得到了最终需要的上传到 GPU 进行运算的矩阵数据
    173             var mvpMatrix:Matrix3D = new Matrix3D();
    174             mvpMatrix.append(_modelViewMatrix);
    175             mvpMatrix.append(_projectionMatrix);
    176             //将我们的最终矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
    177             _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mvpMatrix, true);
    178         }
    179         
    180         private function initProgram():void
    181         {
    182             //顶点着色器代码, 每个上传的顶点前都会执行一次该代码
    183             var vertexArr:Array =
    184                     [
    185                         //op 代表位置输出寄存器, 无论对顶点进行多少次的运算最终都要将结果
    186                         //赋值给他, 这里和我们的正交矩阵进行相乘的运算
    187                         "m44 op, va0, vc0",
    188                         //片段着色器需要用的数据要在这里通过 v0 中转一下, 因为片段着色器不
    189                         //能直接读取 va0 和 va1 的数据
    190                         "mov v0, va1"
    191                     ];
    192             
    193             //片段着色器代码, 每个可以显示的像素都会执行一次该代码
    194             var fragmentArr:Array =
    195                     [
    196                         //对纹理 fs0 进行取样, 通过 v0 代表的 uv 坐标来获取对应的像素点颜
    197                         //色, 将该颜色值存储到 ft0 中
    198                         "tex ft0, v0, fs0 <2d,repeat,linear,nomip>",
    199                         //oc 代表颜色输出寄存器, 每个顶点的颜色数据都要赋值给他
    200                         "mov oc, ft0"
    201                     ];
    202             
    203             //使用 Adobe 自己提供的编译器编译代码为程序可使用的二进制数据
    204             var assembler:AGALMiniAssembler = new AGALMiniAssembler();
    205             _program3D = assembler.assemble2(_context3D, 1, vertexArr.join("
    "), fragmentArr.join("
    "));
    206             
    207             //----- 这段代码是从 render 里搬过来的, 因为不会进行改动就不放在帧循环中了 -----
    208             
    209             //指定着色器代码的 va0 代表的数据段, 表示顶点的 x, y 坐标
    210             _context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
    211             //指定着色器代码的 va1 代表的数据段, 表示顶点的 u, v 数据
    212             _context3D.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
    213             //指定上传的纹理由 fs0 表示
    214             _context3D.setTextureAt(0, _texture);
    215             //指定当前使用的着色器对象
    216             _context3D.setProgram(_program3D);
    217         }
    218         
    219         private function render(event:Event):void
    220         {
    221             //清除已绘制过的 3D 图像
    222             _context3D.clear();
    223             //通过顶点索引数据绘制所有的三角形
    224             _context3D.drawTriangles(_indexBuffer);
    225             //将后台缓冲的图像显示到屏幕
    226             _context3D.present();
    227         }
    228     }
    229 }
  • 相关阅读:
    Mysql常用语句
    关于nginx的一个错误操作记录
    windows下安装ElasticSearch
    在IIS托管服务中设置Rewrite重定向到webapi接口
    asp.net IHttpHandler浅析
    asp.net IHttpModule浅析
    vscode 创建.net core项目初体验
    批量联表更新
    mysql 关于数据库和数据表的基本操作
    mysql Navicat客户端
  • 原文地址:https://www.cnblogs.com/hammerc/p/4074553.html
Copyright © 2011-2022 走看看