API Docs for:
Show:

File: ../src/render/renderFrameContext.js

  1. ///@INFO: BASE
  2. /** RenderFrameContext
  3. * This class is used when you want to render the scene not to the screen but to some texture for postprocessing
  4. * It helps to create the textures and bind them easily, add extra buffers or show it on the screen.
  5. * Check the FrameFX and CameraFX components to see it in action.
  6. * Dependencies: LS.Renderer (writes there only)
  7. *
  8. * @class RenderFrameContext
  9. * @namespace LS
  10. * @constructor
  11. */
  12. function RenderFrameContext( o )
  13. {
  14. this.width = 0; //0 means the same size as the viewport, negative numbers mean reducing the texture in half N times
  15. this.height = 0; //0 means the same size as the viewport
  16. this.precision = RenderFrameContext.DEFAULT_PRECISION; //LOW_PRECISION uses a byte, MEDIUM uses a half_float, HIGH uses a float, or directly the texture type (p.e gl.UNSIGNED_SHORT_4_4_4_4 )
  17. this.filter_texture = true; //magFilter: in case the texture is shown, do you want to see it pixelated?
  18. this.format = GL.RGB; //how many color channels, or directly the texture internalformat
  19. this.use_depth_texture = true; //store the depth in a texture
  20. this.use_stencil_buffer = false; //add an stencil buffer (cannot be read as a texture in webgl)
  21. this.num_extra_textures = 0; //number of extra textures in case we want to render to several buffers
  22. this.name = null; //if a name is provided all the textures will be stored in the LS.ResourcesManager
  23.  
  24. this.generate_mipmaps = false; //try to generate mipmaps if possible (only when the texture is power of two)
  25. this.adjust_aspect = false; //when the size doesnt match the canvas size it could look distorted, settings this to true will fix the problem
  26. this.clone_after_unbind = false; //clones the textures after unbinding it. Used when the texture will be in the 3D scene
  27.  
  28. this._fbo = null;
  29. this._color_texture = null;
  30. this._depth_texture = null;
  31. this._textures = []; //all color textures (the first will be _color_texture)
  32. this._cloned_textures = null; //in case we set the clone_after_unbind to true
  33.  
  34. this._version = 1; //to detect changes
  35. this._minFilter = gl.NEAREST;
  36.  
  37. if(o)
  38. this.configure(o);
  39. }
  40.  
  41. RenderFrameContext.current = null;
  42. RenderFrameContext.stack = [];
  43.  
  44. RenderFrameContext.DEFAULT_PRECISION = 0; //selected by the renderer
  45. RenderFrameContext.LOW_PRECISION = 1; //byte
  46. RenderFrameContext.MEDIUM_PRECISION = 2; //half_float or float
  47. RenderFrameContext.HIGH_PRECISION = 3; //float
  48.  
  49. RenderFrameContext.DEFAULT_PRECISION_WEBGL_TYPE = GL.UNSIGNED_BYTE;
  50.  
  51. RenderFrameContext["@width"] = { type: "number", step: 1, precision: 0 };
  52. RenderFrameContext["@height"] = { type: "number", step: 1, precision: 0 };
  53.  
  54. //definitions for the GUI
  55. RenderFrameContext["@precision"] = { widget: "combo", values: {
  56. "default": RenderFrameContext.DEFAULT_PRECISION,
  57. "low": RenderFrameContext.LOW_PRECISION,
  58. "medium": RenderFrameContext.MEDIUM_PRECISION,
  59. "high": RenderFrameContext.HIGH_PRECISION
  60. }
  61. };
  62.  
  63. RenderFrameContext["@format"] = { widget: "combo", values: {
  64. "RGB": GL.RGB,
  65. "RGBA": GL.RGBA
  66. }
  67. };
  68.  
  69. RenderFrameContext["@num_extra_textures"] = { type: "number", step: 1, min: 0, max: 4, precision: 0 };
  70. RenderFrameContext["@name"] = { type: "string" };
  71.  
  72. RenderFrameContext.prototype.clear = function()
  73. {
  74. if(this.name)
  75. {
  76. for(var i = 0; i < this._textures.length; ++i)
  77. delete LS.ResourcesManager.textures[ this.name + (i > 1 ? i : "") ];
  78. if(this._depth_texture)
  79. delete LS.ResourcesManager.textures[ this.name + "_depth"];
  80. }
  81.  
  82. this._fbo = null;
  83. this._textures = [];
  84. this._color_texture = null;
  85. this._depth_textures = null;
  86. }
  87.  
  88. RenderFrameContext.prototype.configure = function(o)
  89. {
  90. this.width = o.width || 0;
  91. this.height = o.height || 0;
  92. this.format = o.format || GL.RGBA;
  93. this.precision = o.precision || 0;
  94. this.filter_texture = !!o.filter_texture;
  95. this.adjust_aspect = !!o.adjust_aspect;
  96. this.use_depth_texture = !!o.use_depth_texture;
  97. this.use_stencil_buffer = !!o.use_stencil_buffer;
  98. this.num_extra_textures = o.num_extra_textures || 0;
  99. this.name = o.name;
  100. this.clone_after_unbind = !!o.clone_after_unbind;
  101. }
  102.  
  103. RenderFrameContext.prototype.serialize = function()
  104. {
  105. return {
  106. width: this.width,
  107. height: this.height,
  108. filter_texture: this.filter_texture,
  109. precision: this.precision,
  110. format: this.format,
  111. adjust_aspect: this.adjust_aspect,
  112. use_depth_texture: this.use_depth_texture,
  113. use_stencil_buffer: this.use_stencil_buffer,
  114. num_extra_textures: this.num_extra_textures,
  115. clone_after_unbind: this.clone_after_unbind,
  116. name: this.name
  117. };
  118. }
  119.  
  120. RenderFrameContext.prototype.prepare = function( viewport_width, viewport_height )
  121. {
  122. //compute the right size for the textures
  123. var final_width = this.width;
  124. var final_height = this.height;
  125. if(final_width == 0)
  126. final_width = viewport_width;
  127. else if(final_width < 0)
  128. final_width = viewport_width >> Math.abs( this.width ); //subsampling
  129. if(final_height == 0)
  130. final_height = viewport_height;
  131. else if(final_height < 0)
  132. final_height = viewport_height >> Math.abs( this.height ); //subsampling
  133.  
  134. var format = this.format;
  135. var magFilter = this.filter_texture ? gl.LINEAR : gl.NEAREST ;
  136. var type = 0;
  137.  
  138. var minFilter = gl.LINEAR;
  139. if(this.generate_mipmaps && GL.isPowerOfTwo(final_width) && GL.isPowerOfTwo(final_height) )
  140. minFilter = gl.LINEAR_MIPMAP_LINEAR;
  141. this._minFilter = minFilter;
  142.  
  143. switch( this.precision )
  144. {
  145. case RenderFrameContext.LOW_PRECISION:
  146. type = gl.UNSIGNED_BYTE; break;
  147. case RenderFrameContext.MEDIUM_PRECISION:
  148. type = gl.HIGH_PRECISION_FORMAT; break; //gl.HIGH_PRECISION_FORMAT is HALF_FLOAT_OES, if not supported then is FLOAT, otherwise is UNSIGNED_BYTE
  149. case RenderFrameContext.HIGH_PRECISION:
  150. type = gl.FLOAT; break;
  151. case RenderFrameContext.DEFAULT_PRECISION:
  152. type = RenderFrameContext.DEFAULT_PRECISION_WEBGL_TYPE; break;
  153. default:
  154. type = this.precision; break; //used for custom formats
  155. }
  156.  
  157. var textures = this._textures;
  158.  
  159. //for the color: check that the texture size matches
  160. if( !this._color_texture ||
  161. this._color_texture.width != final_width || this._color_texture.height != final_height ||
  162. this._color_texture.type != type || this._color_texture.format != format || this._color_texture.minFilter != minFilter )
  163. this._color_texture = new GL.Texture( final_width, final_height, { minFilter: minFilter, magFilter: magFilter, format: format, type: type });
  164. else
  165. this._color_texture.setParameter( gl.TEXTURE_MAG_FILTER, magFilter );
  166. textures[0] = this._color_texture;
  167.  
  168. //extra color texture (multibuffer rendering)
  169. var total_extra = Math.min( this.num_extra_textures, 4 );
  170. //extra buffers not supported in this webgl context
  171. if(gl.webgl_version == 1 && !gl.extensions["WEBGL_draw_buffers"])
  172. total_extra = 0;
  173.  
  174. for(var i = 0; i < total_extra; ++i) //MAX is 4
  175. {
  176. var extra_texture = textures[1 + i];
  177. if( (!extra_texture || extra_texture.width != final_width || extra_texture.height != final_height || extra_texture.type != type || extra_texture.format != format || extra_texture.minFilter != minFilter) )
  178. extra_texture = new GL.Texture( final_width, final_height, { minFilter: minFilter, magFilter: magFilter, format: format, type: type });
  179. else
  180. extra_texture.setParameter( gl.TEXTURE_MAG_FILTER, magFilter );
  181. textures[1 + i] = extra_texture;
  182. }
  183.  
  184. //for the depth
  185. var depth_format = gl.DEPTH_COMPONENT;
  186. var depth_type = gl.UNSIGNED_INT;
  187.  
  188. if(this.use_stencil_buffer && gl.extensions.WEBGL_depth_texture)
  189. {
  190. depth_format = gl.DEPTH_STENCIL;
  191. depth_type = gl.extensions.WEBGL_depth_texture.UNSIGNED_INT_24_8_WEBGL;
  192. }
  193.  
  194. if( this.use_depth_texture &&
  195. (!this._depth_texture || this._depth_texture.width != final_width || this._depth_texture.height != final_height || this._depth_texture.format != depth_format || this._depth_texture.type != depth_type ) &&
  196. gl.extensions["WEBGL_depth_texture"] )
  197. this._depth_texture = new GL.Texture( final_width, final_height, { filter: gl.NEAREST, format: depth_format, type: depth_type });
  198. else if( !this.use_depth_texture )
  199. this._depth_texture = null;
  200.  
  201. //we will store some extra info in the depth texture for the near and far plane distances
  202. if(this._depth_texture)
  203. {
  204. if(!this._depth_texture.near_far_planes)
  205. this._depth_texture.near_far_planes = vec2.create();
  206. }
  207.  
  208. //create FBO
  209. if( !this._fbo )
  210. this._fbo = new GL.FBO();
  211.  
  212. //cut extra
  213. textures.length = 1 + total_extra;
  214.  
  215. //assign textures (this will enable the FBO but it will restore the old one after finishing)
  216. this._fbo.stencil = this.use_stencil_buffer;
  217. this._fbo.setTextures( textures, this._depth_texture );
  218. this._version += 1;
  219. }
  220.  
  221. /**
  222. * Called to bind the rendering to this context, from now on all the render will be stored in the textures inside
  223. *
  224. * @method enable
  225. */
  226. RenderFrameContext.prototype.enable = function( render_settings, viewport, camera )
  227. {
  228. viewport = viewport || gl.viewport_data;
  229.  
  230. //create FBO and textures (pass width and height of current viewport)
  231. this.prepare( viewport[2], viewport[3] );
  232.  
  233. if(!this._fbo)
  234. throw("No FBO created in RenderFrameContext");
  235.  
  236. //enable FBO
  237. RenderFrameContext.enableFBO( this._fbo, this.adjust_aspect );
  238.  
  239. if(LS.RenderFrameContext.current)
  240. RenderFrameContext.stack.push( LS.RenderFrameContext.current );
  241. LS.RenderFrameContext.current = this;
  242.  
  243. //set depth info inside the texture
  244. camera = camera || LS.Renderer._current_camera;
  245. if(this._depth_texture && camera)
  246. {
  247. this._depth_texture.near_far_planes[0] = camera.near;
  248. this._depth_texture.near_far_planes[1] = camera.far;
  249. }
  250. }
  251.  
  252. //we cannot read and write in the same buffer, so we need to clone the textures
  253. //done from... ?
  254. RenderFrameContext.prototype.cloneBuffers = function()
  255. {
  256. //we do not call this._fbo.unbind because it will set the previous FBO
  257. gl.bindFramebuffer( gl.FRAMEBUFFER, null );
  258.  
  259. ///for every color texture
  260. if( this._textures.length )
  261. {
  262. if(!this._cloned_textures)
  263. this._cloned_textures = [];
  264. var textures = this._textures;
  265. this._cloned_textures.length = textures.length;
  266. for(var i = 0; i < textures.length; ++i)
  267. {
  268. var texture = textures[i];
  269. var cloned_texture = this._cloned_textures[i];
  270. if( !cloned_texture || cloned_texture.hasSameSize( texture[i] ) || !cloned_texture.hasSameProperties( texture ) )
  271. cloned_texture = this._cloned_textures[i] = new GL.Texture( texture.width, texture.height, texture.getProperties() );
  272. texture.copyTo( cloned_texture );
  273. if(i == 0)
  274. LS.ResourcesManager.textures[":color_buffer" ] = cloned_texture;
  275. }
  276. }
  277.  
  278. //for depth
  279. if( this._depth_texture )
  280. {
  281. var depth = this._depth_texture;
  282. if(!this._cloned_depth_texture || this._cloned_depth_texture.width != depth.width || this._cloned_depth_texture.height != depth.height || !this._cloned_depth_texture.hasSameProperties( depth ) )
  283. this._cloned_depth_texture = new GL.Texture( depth.width, depth.height, depth.getProperties() );
  284.  
  285. depth.copyTo( this._cloned_depth_texture );
  286. LS.ResourcesManager.textures[":depth_buffer" ] = this._cloned_depth_texture;
  287. }
  288.  
  289. //rebind FBO
  290. gl.bindFramebuffer( gl.FRAMEBUFFER, this._fbo.handler );
  291. }
  292.  
  293. /**
  294. * Called to stop rendering to this context
  295. *
  296. * @method disable
  297. */
  298. RenderFrameContext.prototype.disable = function()
  299. {
  300. //sets some global parameters for aspect and current RFC
  301. RenderFrameContext.disableFBO( this._fbo );
  302.  
  303. //if we need to store the textures in the ResourcesManager
  304. if(this.name)
  305. {
  306. var textures = this._textures;
  307. for(var i = 0; i < textures.length; ++i)
  308. {
  309. var name = this.name + (i > 0 ? i : "");
  310. textures[i].filename = name;
  311. var final_texture = textures[i];
  312.  
  313. //only clone main color if requested
  314. if( this.clone_after_unbind && i === 0 )
  315. {
  316. if( !this._cloned_texture ||
  317. this._cloned_texture.width !== final_texture.width ||
  318. this._cloned_texture.height !== final_texture.height ||
  319. this._cloned_texture.type !== final_texture.type )
  320. this._cloned_texture = final_texture.clone();
  321. else
  322. final_texture.copyTo( this._cloned_texture );
  323. final_texture = this._cloned_texture;
  324. }
  325.  
  326. if( this._minFilter == gl.LINEAR_MIPMAP_LINEAR )
  327. {
  328. final_texture.bind(0);
  329. gl.generateMipmap(gl.TEXTURE_2D);
  330. final_texture.has_mipmaps = true;
  331. }
  332.  
  333. LS.ResourcesManager.textures[ name ] = final_texture;
  334. }
  335.  
  336. if(this._depth_texture)
  337. {
  338. var name = this.name + "_depth";
  339. this._depth_texture.filename = name;
  340. LS.ResourcesManager.textures[ name ] = this._depth_texture;
  341. //LS.ResourcesManager.textures[ ":depth" ] = this._depth_texture;
  342. }
  343. }
  344.  
  345. if( RenderFrameContext.stack.length )
  346. LS.RenderFrameContext.current = RenderFrameContext.stack.pop();
  347. else
  348. LS.RenderFrameContext.current = null;
  349. }
  350.  
  351. /**
  352. * returns the texture containing the data rendered in this context
  353. *
  354. * @method getColorTexture
  355. * @param {number} index the number of the texture (in case there is more than one)
  356. * @return {GL.Texture} the texture
  357. */
  358. RenderFrameContext.prototype.getColorTexture = function(num)
  359. {
  360. return this._textures[ num || 0 ] || null;
  361. }
  362.  
  363. /**
  364. * returns the depth texture containing the depth data rendered in this context (in case the use_depth_texture is set to true)
  365. *
  366. * @method getDepthTexture
  367. * @return {GL.Texture} the depth texture
  368. */
  369. RenderFrameContext.prototype.getDepthTexture = function()
  370. {
  371. return this._depth_texture || null;
  372. }
  373.  
  374. /**
  375. * Fills the textures with a flat color
  376. * @method clearTextures
  377. */
  378. RenderFrameContext.prototype.clearTextures = function()
  379. {
  380. for(var i = 0; i < this._textures.length; ++i)
  381. {
  382. var texture = this._textures[i];
  383. if(!texture)
  384. continue;
  385. texture.fill([0,0,0,0]);
  386. }
  387. }
  388.  
  389. //enables the FBO and sets every texture with a flag so it cannot be used during the rendering process
  390. RenderFrameContext.enableFBO = function( fbo, adjust_aspect )
  391. {
  392. fbo.bind( true ); //changes viewport to full FBO size (saves old)
  393.  
  394. LS.Renderer._full_viewport.set( gl.viewport_data );
  395. if( adjust_aspect )
  396. {
  397. fbo._old_aspect = LS.Renderer.global_aspect;
  398. LS.Renderer.global_aspect = (gl.canvas.width / gl.canvas.height) / (fbo.color_textures[0].width / fbo.color_textures[0].height);
  399. }
  400. else
  401. delete fbo._old_aspect;
  402. }
  403.  
  404. RenderFrameContext.disableFBO = function( fbo )
  405. {
  406. fbo.unbind(); //restores viewport to old saved one
  407. LS.Renderer._full_viewport.set( fbo._old_viewport );
  408. if( fbo._old_aspect )
  409. LS.Renderer.global_aspect = fbo._old_aspect;
  410. }
  411.  
  412.  
  413. /**
  414. * Render the context of the context to the viewport (allows to apply FXAA)
  415. *
  416. * @method show
  417. * @param {boolean} use_antialiasing in case you want to render with FXAA antialiasing
  418. */
  419. RenderFrameContext.prototype.show = function( use_antialiasing )
  420. {
  421. var texture = this._color_texture;
  422. if(!use_antialiasing)
  423. {
  424. texture.toViewport();
  425. return;
  426. }
  427.  
  428. var viewport = gl.getViewport();
  429. var shader = GL.Shader.getFXAAShader();
  430. var mesh = GL.Mesh.getScreenQuad();
  431. texture.bind(0);
  432. shader.uniforms( { u_texture:0, uViewportSize: viewport.subarray(2,4), u_iViewportSize: [1 / texture.width, 1 / texture.height]} ).draw( mesh );
  433. }
  434.  
  435. //Resets the current WebGL fbo so it renders to the screen
  436. RenderFrameContext.reset = function()
  437. {
  438. gl.bindFramebuffer( gl.FRAMEBUFFER, null );
  439. LS.RenderFrameContext.current = null;
  440. LS.RenderFrameContext.stack.length = 0;
  441. }
  442.  
  443.  
  444. LS.RenderFrameContext = RenderFrameContext;
  445.