吾知网

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 7942|回复: 1
打印 上一主题 下一主题

Stage3D入门实例

[复制链接]
跳转到指定楼层
楼主
发表于 2016-8-22 16:58:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

前提
必备知识
熟悉Stage3D API,对其结构基本了解。对顶点和片段着色器,着色器语言基本了解。在阅读本文之前,强烈建议将本系列的前三篇教程完成(1、Stage3D如何工作 2、顶点和片段着色器 3、何为AGAL

所需额外产品
Flex 4.5 SDK
Download

Flayer Player 11中的playerglobal.swc文件
Download

Flash Player 11
Download

用户级别
中级

所需软件
Flash Builder 4.5 (Download trial)
Flash Professional CS5.5(Download trial)

实例文件
hello_triangle_textured.zip
hello_triangle_colored.zip

在本章中,你将完成一个基于Stage3D API的ActionScript应用。首先你将了解如何搭建Stage3D开发环境。一旦示例工作搭建完成,你将了解如果用AS来初始化 Stage3D,如果创建和渲染由一个超级简单的3D场景,此场景中只包含一个带颜色的三角形。最后,你将一窥为3D图形应用纹理映射的过程。

搭建编译环境

当开始编写Stage3D应用时,我们首先要做的就是搭建编译环境。当然我们可以使用Flex SDK中命令从命令行编译Stage3D应用,但一般来说咱们使用一个比较方便IDE就好了,比如 Flash Builder 4.5。首先从下载和安装Flex SDK(4.5.0.20967或更高)开始,从本文开始部分的链接地址中下载。接下来,从链接中下载Flash Player 11版本的playerglobal.swc文件,并将其拷贝到你刚刚下载的Flex SDK的以下位置:

  • <Flex SDK root folder>\frameworks\libs\player\11.0

复制代码

注意:你需要手动创建名为11.0的文件夹,然后将SWC拷贝进去。如果需要的话,将文件命名为playerglobal.swc。SDK的下一个版本会默认包含playerglobal.swc,所以当SDK升级以后,这一步就不需要了。
以上做完之后,将最新的Flex 4.5 SDK加入到 Flash Builder 4.5编译环境中:选中Preferences > Flash Builder > Installed Flex SDKs,将Flex 4.5 SDK加入。
很显然,你还需要下载带有Stage3D特性的Flash Player 11。用本文最开始的前提部分链接下载吧。
既然你已经建立了带有playerglobal.swc的Flex SDK,并且将其加入了Flash Builder,现在让我们准备创建一个新的ActionScript工程吧!接下来,你还需要配置你的工程让其指向SWF版本13。打开工程属性,点击 ActionScript Compiler页签,在”Additional compiler arguments”输入框中加入“-swf-version=13”。然后在本页签中检查应用是否指向了Flash Player 版本 11。最后,为了让Flash Player能够使用3D硬件加速,你需要将WMODE属性设置为“direct”。在Flash Builder中,打开index.template.html文件,找到params的位置,添加如下代码:

  • params.wmode = “direct”;

复制代码

以下截图显示了添加代码的位置:

图1:在index.template.html中设置wmode=”direct”。

以上设置只针对Flash Player中的ActionScript,在AIR应用中,你需要更改应用的描述符,并将renderMode修改为direct。
编译工程确保设置无误,此时应用应该只显示一个黑色的窗口。

初始化Stage3D

现在你已经将ActionScript应用创建了出来,接下来要做的第一件事就是初始化 Stage3D。
为了执行3D渲染,你需要一个Context3D类的实例,让其负责基本上所有的渲染行为。在构造函数中,加入一下代码:

  • public function HelloTriangleColored()
  • {
  •         stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initMolehill );
  •         stage.stage3Ds[0].requestContext3D();
  • }

复制代码

以上代码用Stage3D API请求一个Context3D实例,并且注册了一个监听器。当Context3D创建完成后,会触发initMolehill
函数。
在initMolehill函数的开头,首先要用Context3D的configureBackBuffer 方法来配置Context3D,代码如下:

  • protected function initMolehill(e:Event):void
  • {
  •         context3D = stage.stage3Ds[0].context3D;
  •         context3D.configureBackBuffer(800, 600, 2, true);
  •         …
  • }

复制代码

以上代码表示,场景渲染大小为800乘以600像素,最小等级抗锯齿(第三个参数),并且为渲染表面创建深度和模版缓冲(第四个参数)。

创建一个带颜色的三角形

在本节中,你将创建一些3D几何图形(将被渲染的3D对象),在本例中,我们来创建一个最简单的带颜色的三角形。为了完成以上任务,需要一个 Vertext Buffer,并且指定顶点的位置(x,y,z)及颜色(r,g,b)信息。每个顶点都有6个分量,将顶点数据定义成一个Vector,如下:

  • protected function initMolehill(e:Event):void
  • {
  •         …
  •         var vertices:Vector.<Number> = Vector.<Number>([
  •         -0.3,-0.3,0, 1, 0, 0, // x, y, z, r, g, b
  •         -0.3, 0.3, 0, 0, 1, 0,
  •         0.3, 0.3, 0, 0, 0, 1]);
  •         …
  • }

复制代码

然后,创建一个VertextBuffer3D实例,以便将Vertext Buffer上传至GPU。

  • protected var vertexbuffer:VertexBuffer3D;
  • protected function initMolehill(e:Event):void
  • {
  •         …
  •         // Create VertexBuffer3D. 3 vertices, of 6 Numbers each
  •         vertexbuffer:VertexBuffer3D = context3D.createVertexBuffer(3, 6);
  •         // Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
  •         vertexbuffer.uploadFromVector(vertices, 0, 3);
  •         …
  • }

复制代码

我们还需要一个Index Buffer来定义三角形。在本例中,这个三角形分别由顶点0,1,2组成。Index Buffer也需要上传至GPU。用IndexBuffer3D类来实现:

  • protected var indexbuffer:IndexBuffer3D;
  • protected function initMolehill(e:Event):void
  • {
  •         …
  •         var indices:Vector.<uint> = Vector.<uint>([0, 1, 2]);
  •         // Create IndexBuffer3D. Total of 3 indices. 1 triangle of 3 vertices
  •         indexbuffer = context3D.createIndexBuffer(3);
  •         // Upload IndexBuffer3D to GPU. Offset 0, count 3
  •         indexbuffer.uploadFromVector (indices, 0, 3);
  •         …
  • }

复制代码

以上代码定义了此图形。现在让我们来创建顶点和片段着色器吧。简单起见, 我们使用了本系列上一讲(什么是AGAL)中的着色器程序。顶点着色器只是根据传入的变换矩阵对顶点进行简单的变换,然后将顶点颜色信息向下传输给片段着色器。

  • m44 op, va0, vc0
  • mov v0, va1

复制代码

片段着色器计算插值颜色并其输出:

  • mov oc, v0

复制代码

你需要AGAL Mini  Assembler将着色器源代码编译成目标代码,然后用Program3D API将着色器上传至GPU。

  • protected function initMolehill(e:Event):void
  • {
  •         …
  • var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
  •         vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
  •                 “m44 op, va0, vc0\n” + // pos to clipspace
  •                 “mov v0, va1″ // copy color
  •         );
  • var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
  •         fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
  •                 “mov oc, v0 “
  •         );
  •         program = context3D.createProgram();
  • program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
  •         …
  • }

复制代码

渲染场景

接下来然我们来渲染3D场景吧。在本节中,我们将建立一个渲染的循环,建立一个onRender函数,当ENTER_FRAME事件发生时对其进行调用。

  • protected function onRender(e:Event):void
  • {
  •         if ( !context3D )
  •                 return;
  •         …
  • }

复制代码

在每次渲染之前,我们要调用Context3D::clear方法,将渲染颜色缓冲(渲染对象的表面颜色)清除,并替换成我们传入的背景色。用以下代码来穿入一个白色的背景:

  • protected function onRender(e:Event):void
  • {
  •         …
  •         context3D.clear ( 1, 1, 1, 1 );
  •         …
  • }

复制代码

渲染的每一帧,我们都要启用带有着色器的Program3D,还有包含定点属性的VertexBuffer3D。我们还需要传入一个变换矩阵,此矩阵被顶点着色器使用。让我们传入一个旋转矩阵,以便使三角形每一帧都有不同的变化。

  • protected function onRender(e:Event):void
  • {
  •                         …
  •         // vertex position to attribute register 0
  •         context3D.setVertexBufferAt (0, vertexbuffer, 0,         Context3DVertexBufferFormat.FLOAT_3);
  •         // color to attribute register 1
  •         context3D.setVertexBufferAt(1, vertexbuffer, 3,         Context3DVertexBufferFormat.FLOAT_3);
  •         // assign shader program
  •         context3D.setProgram(program);
  •         var m:Matrix3D = new Matrix3D();
  •         m.appendRotation(getTimer()/40, Vector3D.Z_AXIS);
  •         context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
  •         …
  • }

复制代码

当以上步骤设置完成后,就可以执行真正的渲染了。调用Context3D的drawTriangles()方法,将Index Buffer传入;这个操作会将三角形渲染到表面(颜色缓冲)。
最后,当一帧中的所有对象都渲染完成后(在这个例子中,只有一个),调用Context3D的present方法,告诉Stage3D应用已经将此帧渲染完成,可以将其呈现在屏幕上了。

  • protected function onRender(e:Event):void
  • {
  •         …
  •         context3D.drawTriangles(indexbuffer);
  •         context3D.present();
  • }

复制代码

运行此工程,查看最终效果如下:

以下是完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package
{
import com.adobe.utils.AGALMiniAssembler;

import flash.display.Sprite;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.utils.getTimer;

[SWF(width="800", height="600", frameRate="60", backgroundColor="#FFFFFF")]
public class AGAL extends Sprite
{
protected var context3D:Context3D;
protected var program:Program3D;
protected var vertexbuffer:VertexBuffer3D;
protected var indexbuffer:IndexBuffer3D;

public function AGAL()
{
stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initMolehill );
stage.stage3Ds[0].requestContext3D();

addEventListener(Event.ENTER_FRAME, onRender);

}

protected function initMolehill(e:Event):void
{
context3D = stage.stage3Ds[0].context3D;
context3D.configureBackBuffer(800, 600, 1, true);

var vertices:Vector.<Number> = Vector.<Number>([
-0.3,-0.3,0, 1, 0, 0, // x, y, z, r, g, b
-0.3, 0.3, 0, 0, 1, 0,
0.3, 0.3, 0, 0, 0, 1]);

// Create VertexBuffer3D. 3 vertices, of 6 Numbers each
vertexbuffer = context3D.createVertexBuffer(3, 6);
// Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
vertexbuffer.uploadFromVector(vertices, 0, 3);

var indices:Vector.<uint> = Vector.<uint>([0, 1, 2]);

// Create IndexBuffer3D. Total of 3 indices. 1 triangle of 3 vertices
indexbuffer = context3D.createIndexBuffer(3);
// Upload IndexBuffer3D to GPU. Offset 0, count 3
indexbuffer.uploadFromVector (indices, 0, 3);

var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"m44 op, va0, vc0\n" + // pos to clipspace
"mov v0, va1" // copy color
);

var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,

"mov oc, v0"
);

program = context3D.createProgram();
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
}

protected function onRender(e:Event):void
{
if ( !context3D )
return;

context3D.clear ( 1, 1, 1, 1 );

// vertex position to attribute register 0
context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// color to attribute register 1
context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
// assign shader program
context3D.setProgram(program);

var m:Matrix3D = new Matrix3D();
m.appendRotation(getTimer()/40, Vector3D.Z_AXIS);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);

context3D.drawTriangles(indexbuffer);

context3D.present();
}
}
}



翻译原文地址:http://uh.9ria.com/link.php?url=http://bbs.9ria.com%2Fviewthread.php%3Ftid%3D108015

本文固定链接: http://www.gisthink.com/blog/guoguogis/?p=338 | GUOGUOGIS


沙发
 楼主| 发表于 2016-8-22 18:04:46 | 只看该作者
stage3d 你不知道的巨坑
如果你是大牛,那你没必要看下去。因为你除了觉得自己牛之外,完全不会把别人放在眼里。低调是一种心态
如果你觉得我说的是假的?很简单你可以拿adobe scout 看下我说的以下几点是否会出现。
stage3d,很多人都在用吧,不知道大家知不知道stage3d的坑是什么:
1:纹理上传速度极其的慢。这个在手机上,就是一个大瓶颈。
2:不支持真正的异步纹理上传,这个也是一个瓶颈,每次上传动画都是卡顿的。
3:内存的消耗比其他语言多了50%不知道大家知不知道:
正常情况来算 2048*2048的图内存消耗在16m。而stage3d是24m。cocos2d的是16m。我是自己写的stage3d渲染。这个说实话我不知道它怎么算出来的。16位的纹理是 12m.cocos2d是 8m.ATF纹理 是 6m.而cocos2d的是4m。
4:上传Bitmap这就是一个巨坑。2048*2048的图上传的时候会产生 36m的bitmapdata的内存。加上纹理内存24m。算起来可以吓死你。36m里的一半是可以手动释放的。而另外一个。就不能需要等10秒的时间系统才会自己释放。对于这个我已经找到了办法,可以在不产生bitmapdata内存的情况下,上传到GPU里。5:内存释放问题,在用scout看内存的时候,的确是释放了。但是用Instruments 检测,内存却没有释放,全部堆在了AIR播发器里,意思是as3的内存释放了但是虚拟机的没有释放。。
6:在ios开发里播发器占有内存 15m,as3自带库占用 3m 加上swf 1m
微博:技术交流可以关注我的微博
解决方法:
本帖隐藏的内容1:使用ATF纹理,快有好处但是有弊端,图片质量很难看。
2:ATF默认是支持异步的,但是bytearray的体积也会造成卡顿。所以不是真正的异步。使用ATF并且开启压缩选项,上传时设置异步上穿,占用主线程的时间基本可以无视,后果是异步解码慢的坑。
3:这个不知道是不是3.8的bug。还是什么。(这里说下官方默认是预留了mipmap的内存。所以你懂的。)解决办法是使用RectangleTexture
4:。。。。
5:目前看来到一定的内存时不会继续上升,应该是flash自己保留系统内存。
6:减小swf体积,不要使用嵌套资源。用加载的方式,用完就释放。


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|吾知网 ( 粤ICP备13013563号-1 )

GMT+8, 2024-12-24 00:58 , Processed in 1.093750 second(s), 7 queries , Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表