/src/de/nulldesign/nd2d/materials/Sprite2DBlurMaterial.as

https://bitbucket.org/HopeSky/mars_nd2d · ActionScript · 352 lines · 229 code · 74 blank · 49 comment · 26 complexity · 167b67a2248f652d7252f79995383dac MD5 · raw file

  1. /*
  2. * ND2D - A Flash Molehill GPU accelerated 2D engine
  3. *
  4. * Author: Lars Gerckens
  5. * Copyright (c) nulldesign 2011
  6. * Repository URL: http://github.com/nulldesign/nd2d
  7. * Getting started: https://github.com/nulldesign/nd2d/wiki
  8. *
  9. *
  10. * Licence Agreement
  11. *
  12. * Permission is hereby granted, free of charge, to any person obtaining a copy
  13. * of this software and associated documentation files (the "Software"), to deal
  14. * in the Software without restriction, including without limitation the rights
  15. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  16. * copies of the Software, and to permit persons to whom the Software is
  17. * furnished to do so, subject to the following conditions:
  18. *
  19. * The above copyright notice and this permission notice shall be included in
  20. * all copies or substantial portions of the Software.
  21. *
  22. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  25. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  26. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  27. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  28. * THE SOFTWARE.
  29. */
  30. package de.nulldesign.nd2d.materials {
  31. import com.adobe.utils.AGALMiniAssembler;
  32. import de.nulldesign.nd2d.display.Camera2D;
  33. import de.nulldesign.nd2d.geom.Face;
  34. import de.nulldesign.nd2d.geom.UV;
  35. import de.nulldesign.nd2d.geom.Vertex;
  36. import de.nulldesign.nd2d.materials.shader.Shader2D;
  37. import de.nulldesign.nd2d.materials.shader.ShaderCache;
  38. import de.nulldesign.nd2d.materials.texture.ASpriteSheetBase;
  39. import de.nulldesign.nd2d.materials.texture.SpriteSheet;
  40. import flash.display.Shader;
  41. import flash.display.Stage;
  42. import flash.display3D.Context3D;
  43. import flash.display3D.Context3DProgramType;
  44. import flash.display3D.Context3DTextureFormat;
  45. import flash.display3D.Program3D;
  46. import flash.display3D.textures.Texture;
  47. import flash.geom.Matrix;
  48. import flash.geom.Matrix3D;
  49. import flash.geom.Point;
  50. import flash.geom.Rectangle;
  51. /**
  52. * http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/
  53. */
  54. public class Sprite2DBlurMaterial extends Sprite2DMaterial {
  55. protected const HORIZONTAL_FRAGMENT_SHADER:String =
  56. // -4
  57. "mov ft0, v0 \n" +
  58. "sub ft0.x, ft0.x, fc3.y \n" +
  59. "tex ft1, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  60. "mul ft1, ft1, fc2.x \n" +
  61. // -3
  62. "add ft0.x, ft0.x, fc3.z \n" +
  63. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  64. "mul ft2, ft2, fc2.y \n" +
  65. "add ft1, ft1, ft2 \n" +
  66. // -2
  67. "add ft0.x, ft0.x, fc3.z \n" +
  68. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  69. "mul ft2, ft2, fc2.z \n" +
  70. "add ft1, ft1, ft2 \n" +
  71. // -1
  72. "add ft0.x, ft0.x, fc3.z \n" +
  73. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  74. "mul ft2, ft2, fc2.w \n" +
  75. "add ft1, ft1, ft2 \n" +
  76. // 0
  77. "add ft0.x, ft0.x, fc3.z \n" +
  78. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  79. "mul ft2, ft2, fc3.x \n" +
  80. "add ft1, ft1, ft2 \n" +
  81. // 1
  82. "add ft0.x, ft0.x, fc3.z \n" +
  83. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  84. "mul ft2, ft2, fc2.w \n" +
  85. "add ft1, ft1, ft2 \n" +
  86. // 2
  87. "add ft0.x, ft0.x, fc3.z \n" +
  88. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  89. "mul ft2, ft2, fc2.z \n" +
  90. "add ft1, ft1, ft2 \n" +
  91. // 3
  92. "add ft0.x, ft0.x, fc3.z \n" +
  93. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  94. "mul ft2, ft2, fc2.y \n" +
  95. "add ft1, ft1, ft2 \n" +
  96. // 4
  97. "add ft0.x, ft0.x, fc3.z \n" +
  98. "tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS> \n" +
  99. "mul ft2, ft2, fc2.x \n" +
  100. "add ft1, ft1, ft2 \n" +
  101. "mul ft1, ft1, fc0 \n" +
  102. "add oc, ft1, fc1 \n";
  103. protected var VERTICAL_FRAGMENT_SHADER:String;
  104. protected var horizontalShader:Shader2D;
  105. protected var verticalShader:Shader2D;
  106. protected const MAX_BLUR:uint = 4;
  107. protected var blurredTexture:Texture;
  108. protected var blurredTexture2:Texture;
  109. protected var blurredTextureCam:Camera2D = new Camera2D(1, 1);
  110. protected var activeRenderToTexture:Texture;
  111. protected var blurX:uint;
  112. protected var blurY:uint;
  113. protected const BLUR_DIRECTION_HORIZONTAL:uint = 0;
  114. protected const BLUR_DIRECTION_VERTICAL:uint = 1;
  115. protected var fragmentData:Vector.<Number>;
  116. public function Sprite2DBlurMaterial(blurX:uint = 4, blurY:uint = 4) {
  117. super();
  118. VERTICAL_FRAGMENT_SHADER = HORIZONTAL_FRAGMENT_SHADER.replace("sub ft0.x, ft0.x, fc3.y", "sub ft0.y, ft0.y, fc3.y");
  119. var reg:RegExp = /add ft0.x, ft0.x, fc3.z/g;
  120. VERTICAL_FRAGMENT_SHADER = VERTICAL_FRAGMENT_SHADER.replace(reg, "add ft0.y, ft0.y, fc3.z");
  121. fragmentData = new Vector.<Number>(8, true);
  122. setBlur(blurX, blurY);
  123. }
  124. public function setBlur(blurX:uint = 4, blurY:uint = 4):void {
  125. this.blurX = blurX;
  126. this.blurY = blurY;
  127. drawCalls = Math.max(1, Math.ceil(blurX / MAX_BLUR) + Math.ceil(blurY / MAX_BLUR));
  128. }
  129. protected function updateBlurKernel(radius:uint, direction:uint):void {
  130. fragmentData[0] = 0.0; //0.05; // fc2.x
  131. fragmentData[1] = 0.0; //0.09; // fc2.y
  132. fragmentData[2] = 0.0; //0.12; // fc2.z
  133. fragmentData[3] = 0.0; //0.15; // fc2.w
  134. fragmentData[4] = 1.0; //0.16; // fc3.x
  135. // movement: minus 4 and plus 1 several times...
  136. fragmentData[5] = 4.0 * (1.0 / (direction == BLUR_DIRECTION_HORIZONTAL ? texture.textureWidth : texture.textureHeight)); // fc3.y
  137. fragmentData[6] = 1.0 * (1.0 / (direction == BLUR_DIRECTION_HORIZONTAL ? texture.textureWidth : texture.textureHeight)); // fc3.z
  138. fragmentData[7] = 0.0; // fc3.w
  139. // http://stackoverflow.com/questions/1696113/how-do-i-gaussian-blur-an-image-without-using-any-in-built-gaussian-functions
  140. if(radius == 0) return;
  141. var kernelLen:uint = radius * 2 + 1;
  142. var r:Number = -radius;
  143. var kernel:Array = [];
  144. var twoRadiusSquaredRecip:Number = 1.0 / (2.0 * radius * radius);
  145. var sqrtTwoPiTimesRadiusRecip:Number = 1.0 / (Math.sqrt(2.0 * Math.PI) * radius);
  146. var kernelSum:Number = 0.0;
  147. var i:int = 0;
  148. for(i = 0; i < kernelLen; i++) {
  149. var x:Number = r * r;
  150. kernel[i] = sqrtTwoPiTimesRadiusRecip * Math.exp(-x * twoRadiusSquaredRecip);
  151. r++;
  152. kernelSum += kernel[i];
  153. }
  154. for(i = 0; i < kernelLen; i++) {
  155. kernel[i] /= kernelSum;
  156. }
  157. var idx:uint = 4;
  158. for(i = kernelLen / 2; i >= 0; i--) {
  159. fragmentData[idx--] = kernel[i];
  160. }
  161. }
  162. override protected function prepareForRender(context:Context3D):void {
  163. // there is no ipad1 fragment shader fix for this material yet. so assume, the node is always tinted and use the color transform shader
  164. nodeTinted = true;
  165. super.prepareForRender(context);
  166. context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
  167. if(!blurredTexture) {
  168. blurredTexture = context.createTexture(texture.textureWidth, texture.textureHeight, Context3DTextureFormat.BGRA, true);
  169. }
  170. if(!blurredTexture2) {
  171. blurredTexture2 = context.createTexture(texture.textureWidth, texture.textureHeight, Context3DTextureFormat.BGRA, true);
  172. }
  173. }
  174. protected function renderBlur(context:Context3D, startTri:uint, numTris:uint):void {
  175. activeRenderToTexture = (activeRenderToTexture == blurredTexture ? blurredTexture2 : blurredTexture);
  176. context.setRenderToTexture(activeRenderToTexture, false, 2, 0);
  177. context.clear(0.0, 0.0, 0.0, 0.0);
  178. context.drawTriangles(indexBuffer, startTri * 3, numTris);
  179. context.setTextureAt(0, activeRenderToTexture);
  180. }
  181. override public function render(context:Context3D, faceList:Vector.<Face>, startTri:uint, numTris:uint):void {
  182. generateBufferData(context, faceList);
  183. // set up camera for blurry texture
  184. blurredTextureCam.resizeCameraStage(texture.textureWidth, texture.textureHeight);
  185. blurredTextureCam.x = -texture.textureWidth * 0.5;
  186. blurredTextureCam.y = -texture.textureHeight * 0.5;
  187. // save camera matrix
  188. var savedCamMatrix:Matrix3D = viewProjectionMatrix;
  189. var savedSpriteSheet:ASpriteSheetBase = spriteSheet;
  190. var savedModelMatrix:Matrix3D = modelMatrix;
  191. viewProjectionMatrix = blurredTextureCam.getViewProjectionMatrix();
  192. spriteSheet = null;
  193. modelMatrix = new Matrix3D();
  194. updateBlurKernel(MAX_BLUR, BLUR_DIRECTION_HORIZONTAL);
  195. prepareForRender(context);
  196. activeRenderToTexture = null;
  197. var totalSteps:int;
  198. var i:uint;
  199. // BLUR X
  200. totalSteps = Math.floor(blurX / MAX_BLUR);
  201. for(i = 0; i < totalSteps; i++) {
  202. renderBlur(context, startTri, numTris);
  203. }
  204. if(blurX % MAX_BLUR != 0) {
  205. updateBlurKernel(blurX % MAX_BLUR, BLUR_DIRECTION_HORIZONTAL);
  206. context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
  207. renderBlur(context, startTri, numTris);
  208. }
  209. // BLUR Y
  210. context.setProgram(verticalShader.shader);
  211. updateBlurKernel(MAX_BLUR, BLUR_DIRECTION_VERTICAL);
  212. context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
  213. totalSteps = Math.floor(blurY / MAX_BLUR);
  214. for(i = 0; i < totalSteps; i++) {
  215. renderBlur(context, startTri, numTris);
  216. }
  217. if(blurY % MAX_BLUR != 0) {
  218. updateBlurKernel(blurY % MAX_BLUR, BLUR_DIRECTION_VERTICAL);
  219. context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
  220. renderBlur(context, startTri, numTris);
  221. }
  222. context.setRenderToBackBuffer();
  223. // FINAL PASS
  224. viewProjectionMatrix = savedCamMatrix;
  225. spriteSheet = savedSpriteSheet;
  226. modelMatrix = savedModelMatrix;
  227. updateBlurKernel(0, BLUR_DIRECTION_HORIZONTAL);
  228. prepareForRender(context);
  229. if(blurX == 0 && blurY == 0) {
  230. activeRenderToTexture = texture.getTexture(context);
  231. }
  232. context.setTextureAt(0, activeRenderToTexture);
  233. context.drawTriangles(indexBuffer, startTri * 3, numTris);
  234. clearAfterRender(context);
  235. }
  236. override public function handleDeviceLoss():void {
  237. super.handleDeviceLoss();
  238. blurredTexture = null;
  239. blurredTexture2 = null;
  240. }
  241. override protected function initProgram(context:Context3D):void {
  242. if(!shaderData) {
  243. horizontalShader = ShaderCache.getInstance().getShader(context, this, VERTEX_SHADER, HORIZONTAL_FRAGMENT_SHADER, 4, texture.textureOptions, 0);
  244. verticalShader = ShaderCache.getInstance().getShader(context, this, VERTEX_SHADER, VERTICAL_FRAGMENT_SHADER, 4, texture.textureOptions, 1000);
  245. shaderData = horizontalShader;
  246. }
  247. }
  248. override public function dispose():void
  249. {
  250. if(horizontalShader)
  251. {
  252. horizontalShader.dispose();
  253. horizontalShader = null;
  254. }
  255. if(verticalShader)
  256. {
  257. verticalShader.dispose();
  258. verticalShader = null;
  259. }
  260. if(blurredTexture)
  261. {
  262. blurredTexture.dispose();
  263. blurredTexture = null;
  264. }
  265. if(blurredTexture2)
  266. {
  267. blurredTexture2.dispose();
  268. blurredTexture2 = null;
  269. }
  270. if(activeRenderToTexture)
  271. {
  272. activeRenderToTexture.dispose();
  273. activeRenderToTexture = null;
  274. }
  275. super.dispose();
  276. }
  277. }
  278. }