stage3D 搭建2d圖形引擎 (八) 動態紋理


回顧之前,我們已經實現了顏色填充的四邊形,以及具有紋理貼圖的四邊形。如果僅僅只是這些,那實在是太無聊了,通過這些我們能夠實現的東西無非就是一堆可以動的圖片,當然對於某些需求這已經足夠,但我們並不因此而止步。另一方面,GPU硬件的能力也遠不止如此,這些單調的貼圖四邊形遠遠沒有發揮為其提供的巨大資源。

接下來我們該研究怎樣讓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的類:

  

 1 package psw2d.pass
 2 {
 3     import com.adobe.utils.AGALMiniAssembler;
 4     
 5     import flash.display3D.Context3D;
 6     import flash.display3D.Context3DProgramType;
 7     import flash.display3D.Context3DTextureFormat;
 8     import flash.display3D.IndexBuffer3D;
 9     import flash.display3D.Program3D;
10     import flash.display3D.textures.Texture;
11     import flash.utils.ByteArray;
12 
13     public class Pass
14     {
15         protected static const agal:AGALMiniAssembler = new AGALMiniAssembler();
16         protected var _shaderVertex:String;
17         protected var _shaderFragment:String;
18         protected var _program:Program3D;
19         protected var _context:Context3D;
20         protected var _isRenderToTexture:Boolean;
21         protected var _texture:Texture;
22         
23         public function Pass(context:Context3D, isRenderToTexture:Boolean, width:Number=1, height:Number=1)
24         {
25             _context = context;
26             _isRenderToTexture = isRenderToTexture;
27             if(_isRenderToTexture) _texture = _context.createTexture(width, height, Context3DTextureFormat.BGRA, true);
28         }
29         
30         public function assemble():void
31         {
32             var vertexShader:ByteArray = agal.assemble(Context3DProgramType.VERTEX, _shaderVertex);
33             var fragmentShader:ByteArray = agal.assemble(Context3DProgramType.FRAGMENT, _shaderFragment);
34             _program = _context.createProgram();
35             _program.upload(vertexShader, fragmentShader);
36         }
37         
38         public function render(iBuffer:IndexBuffer3D) : void {
39             if(!_isRenderToTexture) _context.setRenderToBackBuffer();
40             else    _context.setRenderToTexture(_texture, false, 1);
41             
42             _context.clear(0, 0, 0, 1);
43             _context.setProgram(_program);
44             _context.drawTriangles(iBuffer);
45         }
46         
47         public function getTexture() : Texture {    return _texture;    }
48     }
49 }

 

一個Pass對象對應着上圖中的一次渲染,因而他們有自己獨立的shader program,包括vertex shader 和 fragment shader,根據初始化的參isRenderToTexture來決定是繪制到紋理對象還是緩沖區。一般來說,只有最后一個Pass對象需要將數據繪制到緩沖區,而在它之前的則都需要繪制到紋理對象。這是一個抽象基類,我們需要在它的基礎之上構建具有實際意義的Pass類,為此我們需要編寫shader program,下面給出兩個具體的Pass類:

 正弦波:

 1 package psw2d.pass
 2 {
 3     import flash.display3D.Context3D;
 4     
 5     public class PassSinWave extends Pass
 6     {
 7         public function PassSinWave(context:Context3D, isRenderToTexture:Boolean, width:Number=1, height:Number=1)
 8         {
 9             super(context, isRenderToTexture, width, height);
10             
11             _shaderVertex = "m44 op,va0,vc0\n" +
12                 "mov v0,va1";
13             
14             _shaderFragment="tex ft0,v0,fs1<2d,clamp,linear>\n" +
15                 "sub ft0.x,v0.x,fc0.w\n" +
16                 "mul ft0.x,ft0.x,ft0.x\n"+
17                 "sub ft0.y,v0.y,fc0.w\n"+
18                 "mul ft0.y,ft0.y,ft0.y\n"+
19                 "add ft0.z,ft0.x,ft0.y\n"+
20                 "sqt ft0.z,ft0.z\n"+
21                 "mul ft0.z,ft0.z,fc0.x\n"+
22                 "sub ft0.z,ft0.z,fc0.z\n"+
23                 "sin ft0.z,ft0.z\n"+
24                 "mul ft0.z,ft0.z,fc0.y\n"+
25                 "add ft0,v0,ft0.zzz\n"+
26                 "tex oc,ft0,fs0<2d,clamp,linear>\n";
27         }
28     }
29 }

 

灰度:

 1 package psw2d.pass
 2 {
 3     import flash.display3D.Context3D;
 4     
 5     public class PassGrayscale extends Pass
 6     {
 7         public function PassGrayscale(context:Context3D, isRenderToTexture:Boolean, width:Number=1, height:Number=1)
 8         {
 9             super(context, isRenderToTexture, width, height);
10             
11             _shaderVertex = "" +
12                 "m44 op, va0,vc0\n" +
13                 "mov v0, va1\n";
14             
15             _shaderFragment = "" +
16                 "tex ft0, v0, fs0 <2d,linear,clamp>\n" +
17                 "add ft1.x, ft0.x, ft0.y\n" +
18                 "add ft1.x, ft1.x, ft0.z\n" +
19                 "div ft1.x, ft1.x, fc1.w\n" +
20                 "mov ft0.xyz, ft1.xxx\n" +
21                 "mov oc ft0\n";        
22         }
23     }
24 }

 

以下是具體效果:

 

 本文中所用到的案例取自:http://wonderfl.net/c/zQ6L#code_forked,有修改。

源碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM