stage3D 搭建2d图形引擎 (八) 动态纹理
2012-12-20 13:39
260 查看
回顾之前,我们已经实现了颜色填充的四边形,以及具有纹理贴图的四边形。如果仅仅只是这些,那实在是太无聊了,通过这些我们能够实现的东西无非就是一堆可以动的图片,当然对于某些需求这已经足够,但我们并不因此而止步。另一方面,GPU硬件的能力也远不止如此,这些单调的贴图四边形远远没有发挥为其提供的巨大资源。
接下来我们该研究怎样让GPU尽量地发挥它们应有的价值了。但是为了给GPU施加指令,就需要编写shader program,于是你会发现有一个冲突,我们之前的很多显示对象是共享shader program的,但是为了实现更丰富的表现效果,应该让每个对象的shader program独立开来。所以,很明显,我们不能在原有的shader program上动手脚了。
但是如何才能实现shader program的分离呢?不要着急,慢慢来看:
如果你理解了图形引擎的基本原理,你应该很快能看懂这幅图。它描述的是多次绘制,一次呈现的渲染过程,这也是我目前已经实现的。现在的问题是,一旦我们将像素绘制到缓冲区中,我们就(几乎)没有办法来改变它,换句话说我们可以对像素进行操作的阶段只能在绘制执行之前。如果有一种方法,能够让我对绘制出来的像素,即在缓冲区中的像素进行更多的操作,那么结果将会更加的丰富。
为了实现这种功能,stage3D为我们提供了一个接口:
通过该接口可以指定一个纹理对象,并将之后(通过调用Context3D::drawTriangles())绘制的内容绘制到相应的纹理之上。有了这个接口,我们就可以将像素绘制到纹理,纹理在手,天下我有,接下来我们只需要对纹理进行fragment program并绘制到缓冲区,当然你也可以不让它马上进入缓冲区,而是重复上一过程,继续绘制到另一个纹理,然后再进行fragment program,直到你得到了想要的效果。事实上,如下图:
我们可以不断地重复上述的过程,或者通过不同的子过程进行组合,从而创造出丰富的效果的同时,不失程序设计的灵活性。
下面,我们将这一过程进行实现。首先我们建立一个叫做Pass的类:
一个Pass对象对应着上图中的一次渲染,因而他们有自己独立的shader program,包括vertex shader 和 fragment shader,根据初始化的参isRenderToTexture来决定是绘制到纹理对象还是缓冲区。一般来说,只有最后一个Pass对象需要将数据绘制到缓冲区,而在它之前的则都需要绘制到纹理对象。这是一个抽象基类,我们需要在它的基础之上构建具有实际意义的Pass类,为此我们需要编写shader program,下面给出两个具体的Pass类:
正弦波:
灰度:
以下是具体效果:
本文中所用到的案例取自:http://wonderfl.net/c/zQ6L#code_forked,有修改。
源码
接下来我们该研究怎样让GPU尽量地发挥它们应有的价值了。但是为了给GPU施加指令,就需要编写shader program,于是你会发现有一个冲突,我们之前的很多显示对象是共享shader program的,但是为了实现更丰富的表现效果,应该让每个对象的shader program独立开来。所以,很明显,我们不能在原有的shader program上动手脚了。
但是如何才能实现shader program的分离呢?不要着急,慢慢来看:
如果你理解了图形引擎的基本原理,你应该很快能看懂这幅图。它描述的是多次绘制,一次呈现的渲染过程,这也是我目前已经实现的。现在的问题是,一旦我们将像素绘制到缓冲区中,我们就(几乎)没有办法来改变它,换句话说我们可以对像素进行操作的阶段只能在绘制执行之前。如果有一种方法,能够让我对绘制出来的像素,即在缓冲区中的像素进行更多的操作,那么结果将会更加的丰富。
为了实现这种功能,stage3D为我们提供了一个接口:
1 Context3D::setRenderToTexture(texture:flash.display3D.textures:TextureBase, enableDepthAndStencil:Boolean = false, antiAlias:int = 0, surfaceSelector:int = 0):void
通过该接口可以指定一个纹理对象,并将之后(通过调用Context3D::drawTriangles())绘制的内容绘制到相应的纹理之上。有了这个接口,我们就可以将像素绘制到纹理,纹理在手,天下我有,接下来我们只需要对纹理进行fragment program并绘制到缓冲区,当然你也可以不让它马上进入缓冲区,而是重复上一过程,继续绘制到另一个纹理,然后再进行fragment program,直到你得到了想要的效果。事实上,如下图:
我们可以不断地重复上述的过程,或者通过不同的子过程进行组合,从而创造出丰富的效果的同时,不失程序设计的灵活性。
下面,我们将这一过程进行实现。首先我们建立一个叫做Pass的类:
package psw2d.pass { import com.adobe.utils.AGALMiniAssembler; import flash.display3D.Context3D; import flash.display3D.Context3DProgramType; import flash.display3D.Context3DTextureFormat; import flash.display3D.IndexBuffer3D; import flash.display3D.Program3D; import flash.display3D.textures.Texture; import flash.utils.ByteArray; public class Pass { protected static const agal:AGALMiniAssembler = new AGALMiniAssembler(); protected var _shaderVertex:String; protected var _shaderFragment:String; protected var _program:Program3D; protected var _context:Context3D; protected var _isRenderToTexture:Boolean; protected var _texture:Texture; public function Pass(context:Context3D, isRenderToTexture:Boolean, width:Number=1, height:Number=1) { _context = context; _isRenderToTexture = isRenderToTexture; if(_isRenderToTexture) _texture = _context.createTexture(width, height, Context3DTextureFormat.BGRA, true); } public function assemble():void { var vertexShader:ByteArray = agal.assemble(Context3DProgramType.VERTEX, _shaderVertex); var fragmentShader:ByteArray = agal.assemble(Context3DProgramType.FRAGMENT, _shaderFragment); _program = _context.createProgram(); _program.upload(vertexShader, fragmentShader); } public function render(iBuffer:IndexBuffer3D) : void { if(!_isRenderToTexture) _context.setRenderToBackBuffer(); else _context.setRenderToTexture(_texture, false, 1); _context.clear(0, 0, 0, 1); _context.setProgram(_program); _context.drawTriangles(iBuffer); } public function getTexture() : Texture { return _texture; } } }
一个Pass对象对应着上图中的一次渲染,因而他们有自己独立的shader program,包括vertex shader 和 fragment shader,根据初始化的参isRenderToTexture来决定是绘制到纹理对象还是缓冲区。一般来说,只有最后一个Pass对象需要将数据绘制到缓冲区,而在它之前的则都需要绘制到纹理对象。这是一个抽象基类,我们需要在它的基础之上构建具有实际意义的Pass类,为此我们需要编写shader program,下面给出两个具体的Pass类:
正弦波:
package psw2d.pass { import flash.display3D.Context3D; public class PassSinWave extends Pass { public function PassSinWave(context:Context3D, isRenderToTexture:Boolean, width:Number=1, height:Number=1) { super(context, isRenderToTexture, width, height); _shaderVertex = "m44 op,va0,vc0\n" + "mov v0,va1"; _shaderFragment="tex ft0,v0,fs1<2d,clamp,linear>\n" + "sub ft0.x,v0.x,fc0.w\n" + "mul ft0.x,ft0.x,ft0.x\n"+ "sub ft0.y,v0.y,fc0.w\n"+ "mul ft0.y,ft0.y,ft0.y\n"+ "add ft0.z,ft0.x,ft0.y\n"+ "sqt ft0.z,ft0.z\n"+ "mul ft0.z,ft0.z,fc0.x\n"+ "sub ft0.z,ft0.z,fc0.z\n"+ "sin ft0.z,ft0.z\n"+ "mul ft0.z,ft0.z,fc0.y\n"+ "add ft0,v0,ft0.zzz\n"+ "tex oc,ft0,fs0<2d,clamp,linear>\n"; } } }
灰度:
package psw2d.pass { import flash.display3D.Context3D; public class PassGrayscale extends Pass { public function PassGrayscale(context:Context3D, isRenderToTexture:Boolean, width:Number=1, height:Number=1) { super(context, isRenderToTexture, width, height); _shaderVertex = "" + "m44 op, va0,vc0\n" + "mov v0, va1\n"; _shaderFragment = "" + "tex ft0, v0, fs0 <2d,linear,clamp>\n" + "add ft1.x, ft0.x, ft0.y\n" + "add ft1.x, ft1.x, ft0.z\n" + "div ft1.x, ft1.x, fc1.w\n" + "mov ft0.xyz, ft1.xxx\n" + "mov oc ft0\n"; } } }
以下是具体效果:
本文中所用到的案例取自:http://wonderfl.net/c/zQ6L#code_forked,有修改。
源码
相关文章推荐
- stage3D 搭建2d图形引擎 (五)不同纹理的显示对象
- stage3D 搭建2d图形引擎 (七) 简单纹理动画
- stage3D 搭建2d图形引擎 (三)透明度(alpha混合)
- stage3D 搭建2d图形引擎 (四)静态文理贴图
- stage3D 搭建2d图形引擎 (六) 显示列表
- 幻世(OurDream)2D图形引擎使用教程6——使用纹理
- 幻世(OurDream)2D图形引擎教程汇总[2014年3月12日提供教程打包下载链接]
- 新版Ycnd 2D 图形引擎SDK 正式发布!
- 幻世(OurDream)2D图形引擎易语言汉化版更新提示
- cocos2d-x学习笔记(1)--coco2d-x引擎下载及环境搭建
- 幻世(OurDream)2D图形引擎大更新——炫丽粒子特效强势回归!
- 幻世(OurDream)2D图形引擎易语言汉化版更新提示
- 从android角度看2D加速图形引擎
- 幻世(OurDream)2D图形引擎使用教程9——处理操作输入(3)
- 基于Stage3D的开源2D引擎Nd2d
- 幻世(OurDream)TM 2D图形引擎开通捐赠渠道
- 【学习笔记】3D图形核心基础精炼版-4:stage3D实战-环境搭建
- 幻世 OurDream (TM) 2D图形引擎更新版本发布
- 一个c++ 2d图形引擎 AGG
- 幻世(OurDream)2D图形引擎使用教程2——第一个幻世程序