Stage3D学习笔记(五):通过矩阵操作纹理
虽然我们上一节已经实现了正交矩阵的显示,但是可以明显的感觉到要调整显示纹理的坐标和尺寸是相当复杂的,需要对每个顶点进行操作,如果还要加上注册点和旋转的话,用上一节的方法来做是会让人发疯的!
所以我们距离实用还有很长的路要走,加上之前面试时由于对Starling内部实现的不了解导致的失利,所以接下来的一段学习笔记会专注在通过正交矩阵来实现2D框架的实现上,并参考Starling的架构做一个精简的山寨版!
首要任务,就是要能方便的对纹理设置位置、尺寸和旋转!下面介绍Starling中使用的方法,借助Flash原生的Matrix3D类来实现对纹理的操作。
我们基于Stage3D学习笔记(三)的代码来修改;
首先我们除了需要一个正交矩阵用来进行正交运算外,还需要一个矩阵用来记录模型的信息(我们可以把显示的纹理看做由两个三角面拼成的一个四方形模型)。
//正交矩阵
private var _projectionMatrix:Matrix3D;
//模型矩阵, 通过操作该矩阵来变换纹理的显示
private var _modelViewMatrix:Matrix3D;
正交矩阵的初始化相对于上一节有所变动,为了清晰没有使用新版Starling的简化过的矩阵,而是使用第一版的矩阵。
private function initOrthographicProjection(width:Number, height:Number, near:Number = -1.0, far:Number = 1.0):void
{
//创建正交矩阵的实例
_projectionMatrix = new Matrix3D();
//设置正交矩阵数据, 这个公式记死即可
var coords:Vector.<Number> = new <Number>
[
2.0 / width, 0.0, 0.0, 0.0,
0.0, -2.0 / height, 0.0, 0.0,
0.0, 0.0, -2.0 / (far - near), 0.0,
-1.0, 1.0, -(far + near) / (far - near), 1.0
];
_projectionMatrix.copyRawDataFrom(coords);
}
还有一点需要更改的是,由于我们的模型的尺寸选择等信息都交由_modelViewMatrix对象来存储,所以顶点数据可以不需要修改,因为我们的需求只有一个,就是显示一个四边形的图片,所以顶点数据可以写死,可以去掉z轴信息,uv信息和xy的信息一致,也可以去掉,不过我们这里进行了保留。
private function initBuffer():void
{
//顶点数据, 因为只需要显示一张图片所以这里的顶点数据是可以写死的, 同时可以去掉 z 轴
//的数据, 因为不需要使用到 z 轴, 我们按照下面的规则来排列顶点:
//0 - 1
//| / |
//2 - 3
//有趣的是 uv 的数据和顶点数据其实是一致的, 所以 uv 的数据也可以去除, 不过我们这里
//先留着, Starling 框架中 uv 数据是已经去掉的
var vertexData:Vector.<Number> = Vector.<Number>(
[
// x, y, u, v
0, 0, 0, 0,
1, 0, 1, 0,
0, 1, 0, 1,
1, 1, 1, 1
]);
//省略
}
由于去掉了z轴的数据,所以数据上传的格式也要进行相应的更改,具体可以看最终的代码。
模型矩阵是直接操作当前的纹理的显示的,对他的修改也比较简单:
private function transformMatrix():void
{
//设置纹理的转换矩阵
_modelViewMatrix = new Matrix3D();
//设置矩阵的位置
_modelViewMatrix.prependTranslation(0, 0, 0);
//设置矩阵的旋转
_modelViewMatrix.prependRotation(0, Vector3D.Z_AXIS);
//设置矩阵的尺寸
_modelViewMatrix.prependScale(128, 128, 1);
//设置纹理的中心点
_modelViewMatrix.prependTranslation(0, 0, 0); //将纹理的转换矩阵和正交矩阵结合就得到了最终需要的上传到 GPU 进行运算的矩阵数据
var mvpMatrix:Matrix3D = new Matrix3D();
mvpMatrix.append(_modelViewMatrix);
mvpMatrix.append(_projectionMatrix);
//将我们的最终矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mvpMatrix, true);
}
需要注意的是,模型矩阵和正交矩阵是需要进行合并的。
我们看一下现在的效果:
不错哦,是我们想要的效果,下面测试一下旋转和位移:
private function transformMatrix():void
{
//设置纹理的转换矩阵
_modelViewMatrix = new Matrix3D();
//设置矩阵的位置
_modelViewMatrix.prependTranslation(100, 100, 0);
//设置矩阵的旋转
_modelViewMatrix.prependRotation(45, Vector3D.Z_AXIS);
//设置矩阵的尺寸
_modelViewMatrix.prependScale(128, 128, 1);
//设置纹理的中心点
_modelViewMatrix.prependTranslation(0, 0, 0);
//省略
}
直接给出代码了:
package
{
import com.adobe.utils.AGALMiniAssembler; import flash.display.Bitmap; import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display3D.Context3D;
import flash.display3D.Context3DProfile;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DRenderMode;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.Texture;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Vector3D; [SWF(width=550, height=400, frameRate=60)]
public class Matrix3DTest extends Sprite
{
[Embed(source="img.png")]
private var IMG_CLASS:Class; //3D 场景对象
private var _stage3D:Stage3D;
//3D 上下文渲染对象
private var _context3D:Context3D; //顶点缓冲数据
private var _vertexBuffer:VertexBuffer3D;
//索引缓冲数据
private var _indexBuffer:IndexBuffer3D;
//纹理数据对象
private var _texture:Texture; //着色器对象
private var _program3D:Program3D; //正交矩阵
private var _projectionMatrix:Matrix3D;
//模型矩阵, 通过操作该矩阵来变换纹理的显示
private var _modelViewMatrix:Matrix3D; public function Matrix3DTest()
{
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
} private function addedToStageHandler(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); //3D 场景存在, 一般存在 4 个 3D 场景对象
if(stage.stage3Ds.length > 0)
{
//使用最下层的 3D 场景
_stage3D = stage.stage3Ds[0];
//请求 3D 上下文渲染对象
_stage3D.addEventListener(ErrorEvent.ERROR, stage3DErrorHandler);
_stage3D.addEventListener(Event.CONTEXT3D_CREATE, context3DCreateHandler);
_stage3D.requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.BASELINE);
}
} private function stage3DErrorHandler(event:ErrorEvent):void
{
trace("Context3D对象请求失败:", event.text);
} private function context3DCreateHandler(event:Event):void
{
initContext3D();
initOrthographicProjection(stage.stageWidth, stage.stageHeight);
initBuffer();
initTexture();
transformMatrix();
initProgram(); //每帧进行渲染
addEventListener(Event.ENTER_FRAME, render);
} private function initContext3D():void
{
//获取 3D 渲染对象
_context3D = _stage3D.context3D;
//设置后台缓冲区
_context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2);
} private function initOrthographicProjection(width:Number, height:Number, near:Number = -1.0, far:Number = 1.0):void
{
//创建正交矩阵的实例
_projectionMatrix = new Matrix3D();
//设置正交矩阵数据, 这个公式记死即可
var coords:Vector.<Number> = new <Number>
[
2.0 / width, 0.0, 0.0, 0.0,
0.0, -2.0 / height, 0.0, 0.0,
0.0, 0.0, -2.0 / (far - near), 0.0,
-1.0, 1.0, -(far + near) / (far - near), 1.0
];
_projectionMatrix.copyRawDataFrom(coords);
} private function initBuffer():void
{
//顶点数据, 因为只需要显示一张图片所以这里的顶点数据是可以写死的, 同时可以去掉 z 轴
//的数据, 因为不需要使用到 z 轴, 我们按照下面的规则来排列顶点:
//0 - 1
//| / |
//2 - 3
//有趣的是 uv 的数据和顶点数据其实是一致的, 所以 uv 的数据也可以去除, 不过我们这里
//先留着, Starling 框架中 uv 数据是已经去掉的
var vertexData:Vector.<Number> = Vector.<Number>(
[
// x, y, u, v
0, 0, 0, 0,
1, 0, 1, 0,
0, 1, 0, 1,
1, 1, 1, 1
]); //创建顶点缓冲对象, 参数设定存在几组数据和每组数据的个数
_vertexBuffer = _context3D.createVertexBuffer(vertexData.length / 4, 4);
//上传顶点数据到GPU, 参数设定从第几组数据开始上传和上传多少组数据
_vertexBuffer.uploadFromVector(vertexData, 0, vertexData.length / 4); //索引数据
var indexData:Vector.<uint> = Vector.<uint>(
[
0, 1, 2,
1, 2, 3
]); //创建索引缓冲对象, 每个索引对应顶点数据中的相对应的一组数据,
//每3个索引组成一个会被绘制出来的三角形, 参数指定索引的长度
_indexBuffer = _context3D.createIndexBuffer(indexData.length);
//上传索引数据到GPU, 参数设定从第几个数据开始上传和上传多少个数据
_indexBuffer.uploadFromVector(indexData, 0, indexData.length);
} private function initTexture():void
{
//创建位图
var bitmap:Bitmap = new IMG_CLASS() as Bitmap;
//创建纹理, 注意尺寸必须是 2 的幂数
_texture = _context3D.createTexture(128, 128, Context3DTextureFormat.BGRA, true);
//上传纹理到 GPU, 第二个参数表示该纹理的 mipmap 级别, 级别零是高级全分辨率图像
_texture.uploadFromBitmapData(bitmap.bitmapData, 0);
} private function transformMatrix():void
{
//设置纹理的转换矩阵
_modelViewMatrix = new Matrix3D();
//设置矩阵的位置
_modelViewMatrix.prependTranslation(100, 100, 0);
//设置矩阵的旋转
_modelViewMatrix.prependRotation(45, Vector3D.Z_AXIS);
//设置矩阵的尺寸
_modelViewMatrix.prependScale(128, 128, 1);
//设置纹理的中心点
_modelViewMatrix.prependTranslation(0, 0, 0); //将纹理的转换矩阵和正交矩阵结合就得到了最终需要的上传到 GPU 进行运算的矩阵数据
var mvpMatrix:Matrix3D = new Matrix3D();
mvpMatrix.append(_modelViewMatrix);
mvpMatrix.append(_projectionMatrix);
//将我们的最终矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mvpMatrix, true);
} private function initProgram():void
{
//顶点着色器代码, 每个上传的顶点前都会执行一次该代码
var vertexArr:Array =
[
//op 代表位置输出寄存器, 无论对顶点进行多少次的运算最终都要将结果
//赋值给他, 这里和我们的正交矩阵进行相乘的运算
"m44 op, va0, vc0",
//片段着色器需要用的数据要在这里通过 v0 中转一下, 因为片段着色器不
//能直接读取 va0 和 va1 的数据
"mov v0, va1"
]; //片段着色器代码, 每个可以显示的像素都会执行一次该代码
var fragmentArr:Array =
[
//对纹理 fs0 进行取样, 通过 v0 代表的 uv 坐标来获取对应的像素点颜
//色, 将该颜色值存储到 ft0 中
"tex ft0, v0, fs0 <2d,repeat,linear,nomip>",
//oc 代表颜色输出寄存器, 每个顶点的颜色数据都要赋值给他
"mov oc, ft0"
]; //使用 Adobe 自己提供的编译器编译代码为程序可使用的二进制数据
var assembler:AGALMiniAssembler = new AGALMiniAssembler();
_program3D = assembler.assemble2(_context3D, 1, vertexArr.join("\n"), fragmentArr.join("\n")); //----- 这段代码是从 render 里搬过来的, 因为不会进行改动就不放在帧循环中了 ----- //指定着色器代码的 va0 代表的数据段, 表示顶点的 x, y 坐标
_context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
//指定着色器代码的 va1 代表的数据段, 表示顶点的 u, v 数据
_context3D.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
//指定上传的纹理由 fs0 表示
_context3D.setTextureAt(0, _texture);
//指定当前使用的着色器对象
_context3D.setProgram(_program3D);
} private function render(event:Event):void
{
//清除已绘制过的 3D 图像
_context3D.clear();
//通过顶点索引数据绘制所有的三角形
_context3D.drawTriangles(_indexBuffer);
//将后台缓冲的图像显示到屏幕
_context3D.present();
}
}
}
Stage3D学习笔记(五):通过矩阵操作纹理的更多相关文章
- ES6学习笔记<五> Module的操作——import、export、as
import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...
- MongoDB学习笔记(五) MongoDB文件存取操作
由于MongoDB的文档结构为BJSON格式(BJSON全称:Binary JSON),而BJSON格式本身就支持保存二进制格式的数据,因此可以把文件的二进制格式的数据直接保存到MongoDB的文档结 ...
- 【Stage3D学习笔记续】山寨Starling(八):核心优化(批处理)的实现
批处理是使GPU进行高效绘制的一种技术手段,也是整个渲染流程中最核心的技术,到目前为止我们并没有使用到这种技术手段,下面我们看看我们现在的渲染机制. 先想一想我们最开始是怎么向GPU绘制一幅图像的,可 ...
- (转)Qt Model/View 学习笔记 (五)——View 类
Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...
- Windows phone 8 学习笔记(2) 数据文件操作
原文:Windows phone 8 学习笔记(2) 数据文件操作 Windows phone 8 应用用于数据文件存储访问的位置仅仅限于安装文件夹.本地文件夹(独立存储空间).媒体库和SD卡四个地方 ...
- WebGL three.js学习笔记 6种类型的纹理介绍及应用
WebGL three.js学习笔记 6种类型的纹理介绍及应用 本文所使用到的demo演示: 高光贴图Demo演示 反光效果Demo演示(因为是加载的模型,所以速度会慢) (一)普通纹理 计算机图形学 ...
- 【opencv学习笔记五】一个简单程序:图像读取与显示
今天我们来学习一个最简单的程序,即从文件读取图像并且创建窗口显示该图像. 目录 [imread]图像读取 [namedWindow]创建window窗口 [imshow]图像显示 [imwrite]图 ...
- C++基础 学习笔记五:重载之运算符重载
C++基础 学习笔记五:重载之运算符重载 什么是运算符重载 用同一个运算符完成不同的功能即同一个运算符可以有不同的功能的方法叫做运算符重载.运算符重载是静态多态性的体现. 运算符重载的规则 重载公式 ...
- C#可扩展编程之MEF学习笔记(五):MEF高级进阶
好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...
随机推荐
- CentOS7 64位 自动分配IP地址设置
vim /etc/sysconfig/network-scripts/ifcfg-eno16777736 # ifcfg-后接网卡名称 配置如下,ONBOOT设置为yes HWADDR=:0C::E9 ...
- Hadoop分布式文件系统(HDFS)详解
HDFS简介: 当数据集的大小超过一台独立物理计算机的存储能力时,就有必要对它进行分区 (partition)并存储到若干台单独的计算机上.管理网络中跨多台计算机存储的文件系统成为分布式文件系统 (D ...
- UML各种图画法总结
<UML 2.4.1 教程> http://www.sparxsystems.cn/resources/uml2_tutorial/ <UML总结(对九种图的认识和如何使用Ratio ...
- WinAPI——钩子函数大全2
CallNextHookEx 函数功能:该函数发送挂钩信息给当前挂钩链中的下一个挂钩处理过程,一个挂钩处理过程可在对该挂钩信息进行处理之前或之后调用本函数. 函数原形:LRESULT CallNext ...
- MediaPlayer和AudioTrack播放Audio的区别与联系
转自http://blog.csdn.net/ameyume/article/details/7618820 播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供 ...
- 宏FSP_SEG_INODES_PER_PAGE
#define FSP_SEG_INODES_PER_PAGE(zip_size) \ (((zip_size ? zip_size : UNIV_PAGE_SIZE) \ - FSEG_ARR_OF ...
- bzoj3940: [Usaco2015 Feb]Censoring
AC自动机.为什么洛谷水题赛会出现这种题然而并不会那么题意就不说啦 .终于会写AC自动机判断是否是子串啦...用到kmp的就可以用AC自动机水过去啦 #include<cstdio> #i ...
- ssh-keygen+ssh-copy-id 在linux下实现ssh无密码登录访问
环境: 192.168.2.10 192.168.2.11 实现:2.10 ssh无需密码登录到2.11 在2.10 ssh到2.11机器上,需要密码,这样对一些脚本工作不方便,因为需要密码,也就是需 ...
- 4、什么构成了我们Android应用程序?(七大件)
一.应用程序四大组件 [Activity] Activity是Android应用程序的一个界面,可以通过这个界面查看联系人,打电话戒玩游戏. b. 一个应用程序通常包含多个Activity. c. A ...
- DELPHI 中的Delay函数,利用GetTickCount和Application.ProcessMessages构建
作者 关劲松 delphi 开发中有些时候需要停留片刻,等待界面输入,或异步操作完成,如果使用sleep函数的话,整个程序都会停顿,界面还会出现冻结的情况.因此需要自行编写一个 ...