babylon.lensRenderingPipeline.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /// <reference path="babylon.d.ts" />
  2. module BABYLON {
  3. export class LensRenderingPipeline extends PostProcessRenderPipeline {
  4. // Lens effects can be of the following:
  5. // - chromatic aberration (slight shift of RGB colors)
  6. // - blur on the edge of the lens
  7. // - lens distortion
  8. // - depth-of-field blur & highlights enhancing
  9. // - depth-of-field 'bokeh' effect (shapes appearing in blurred areas)
  10. // - grain effect (noise or custom texture)
  11. // Two additional texture samplers are needed:
  12. // - depth map (for depth-of-field)
  13. // - grain texture
  14. /**
  15. * The chromatic aberration PostProcess id in the pipeline
  16. * @type {string}
  17. */
  18. public LensChromaticAberrationEffect: string = "LensChromaticAberrationEffect";
  19. /**
  20. * The highlights enhancing PostProcess id in the pipeline
  21. * @type {string}
  22. */
  23. public HighlightsEnhancingEffect: string = "HighlightsEnhancingEffect";
  24. /**
  25. * The depth-of-field PostProcess id in the pipeline
  26. * @type {string}
  27. */
  28. public LensDepthOfFieldEffect: string = "LensDepthOfFieldEffect";
  29. private _scene: Scene;
  30. private _depthTexture: RenderTargetTexture;
  31. private _grainTexture: Texture;
  32. private _chromaticAberrationPostProcess: PostProcess;
  33. private _highlightsPostProcess: PostProcess;
  34. private _depthOfFieldPostProcess: PostProcess;
  35. private _edgeBlur: number;
  36. private _grainAmount: number;
  37. private _chromaticAberration: number;
  38. private _distortion: number;
  39. private _highlightsGain: number;
  40. private _highlightsThreshold: number;
  41. private _dofDepth: number;
  42. private _dofAperture: number;
  43. private _dofPentagon: boolean;
  44. private _blurNoise: boolean;
  45. /**
  46. * @constructor
  47. *
  48. * Effect parameters are as follow:
  49. * {
  50. * chromatic_aberration: number; // from 0 to x (1 for realism)
  51. * edge_blur: number; // from 0 to x (1 for realism)
  52. * distortion: number; // from 0 to x (1 for realism)
  53. * grain_amount: number; // from 0 to 1
  54. * grain_texture: BABYLON.Texture; // texture to use for grain effect; if unset, use random B&W noise
  55. * dof_focus_depth: number; // depth-of-field: focus depth; unset to disable (disabled by default)
  56. * dof_aperture: number; // depth-of-field: focus blur bias (default: 1)
  57. * dof_pentagon: boolean; // depth-of-field: makes a pentagon-like "bokeh" effect
  58. * dof_gain: number; // depth-of-field: depthOfField gain; unset to disable (disabled by default)
  59. * dof_threshold: number; // depth-of-field: depthOfField threshold (default: 1)
  60. * blur_noise: boolean; // add a little bit of noise to the blur (default: true)
  61. * }
  62. * Note: if an effect parameter is unset, effect is disabled
  63. *
  64. * @param {string} name - The rendering pipeline name
  65. * @param {object} parameters - An object containing all parameters (see above)
  66. * @param {BABYLON.Scene} scene - The scene linked to this pipeline
  67. * @param {number} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
  68. * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
  69. */
  70. constructor(name: string, parameters: any, scene: Scene, ratio: number = 1.0, cameras?: Camera[]) {
  71. super(scene.getEngine(), name);
  72. this._scene = scene;
  73. // Fetch texture samplers
  74. this._depthTexture = scene.enableDepthRenderer().getDepthMap(); // Force depth renderer "on"
  75. if(parameters.grain_texture) { this._grainTexture = parameters.grain_texture; }
  76. else { this._createGrainTexture(); }
  77. // save parameters
  78. this._edgeBlur = parameters.edge_blur ? parameters.edge_blur : 0;
  79. this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
  80. this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
  81. this._distortion = parameters.distortion ? parameters.distortion : 0;
  82. this._highlightsGain = parameters.dof_gain !== undefined ? parameters.dof_gain : -1;
  83. this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
  84. this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
  85. this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
  86. this._dofPentagon = parameters.dof_pentagon !== undefined ? parameters.dof_pentagon : true;
  87. this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
  88. // Create effects
  89. this._createChromaticAberrationPostProcess(ratio);
  90. this._createHighlightsPostProcess(ratio);
  91. this._createDepthOfFieldPostProcess(ratio);
  92. // Set up pipeline
  93. this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensChromaticAberrationEffect, () => { return this._chromaticAberrationPostProcess; }, true));
  94. this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.HighlightsEnhancingEffect, () => { return this._highlightsPostProcess; }, true));
  95. this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensDepthOfFieldEffect, () => { return this._depthOfFieldPostProcess; }, true));
  96. if(this._highlightsGain == -1) {
  97. this._disableEffect(this.HighlightsEnhancingEffect, null);
  98. }
  99. // Finish
  100. scene.postProcessRenderPipelineManager.addPipeline(this);
  101. if(cameras) {
  102. scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
  103. }
  104. }
  105. // public methods (self explanatory)
  106. public setEdgeBlur(amount: number) { this._edgeBlur = amount; }
  107. public disableEdgeBlur() { this._edgeBlur = 0; }
  108. public setGrainAmount(amount: number) { this._grainAmount = amount; }
  109. public disableGrain() { this._grainAmount = 0; }
  110. public setChromaticAberration(amount: number) { this._chromaticAberration = amount; }
  111. public disableChromaticAberration() { this._chromaticAberration = 0; }
  112. public setEdgeDistortion(amount: number) { this._distortion = amount; }
  113. public disableEdgeDistortion() { this._distortion = 0; }
  114. public setFocusDepth(amount: number) { this._dofDepth = amount; }
  115. public disableDepthOfField() { this._dofDepth = -1; }
  116. public setAperture(amount: number) { this._dofAperture = amount; }
  117. public enablePentagonBokeh() { this._dofPentagon = true; }
  118. public disablePentagonBokeh() { this._dofPentagon = false; }
  119. public enableNoiseBlur() { this._blurNoise = true; }
  120. public disableNoiseBlur() { this._blurNoise = false; }
  121. public setHighlightsGain(amount: number) {
  122. this._highlightsGain = amount;
  123. }
  124. public setHighlightsThreshold(amount: number) {
  125. if(this._highlightsGain == -1) {
  126. this._highlightsGain = 1.0;
  127. }
  128. this._highlightsThreshold = amount;
  129. }
  130. public disableHighlights() {
  131. this._highlightsGain = -1;
  132. }
  133. /**
  134. * Removes the internal pipeline assets and detaches the pipeline from the scene cameras
  135. */
  136. public dispose(disableDepthRender: boolean = false): void {
  137. this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
  138. this._chromaticAberrationPostProcess = undefined;
  139. this._highlightsPostProcess = undefined;
  140. this._depthOfFieldPostProcess = undefined;
  141. this._grainTexture.dispose();
  142. if (disableDepthRender)
  143. this._scene.disableDepthRenderer();
  144. }
  145. // colors shifting and distortion
  146. private _createChromaticAberrationPostProcess(ratio: number): void {
  147. this._chromaticAberrationPostProcess = new PostProcess("LensChromaticAberration", "chromaticAberration",
  148. ["chromatic_aberration", "screen_width", "screen_height"], // uniforms
  149. [], // samplers
  150. ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
  151. this._scene.getEngine(), false);
  152. this._chromaticAberrationPostProcess.onApply = (effect: Effect) => {
  153. effect.setFloat('chromatic_aberration', this._chromaticAberration);
  154. effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
  155. effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
  156. };
  157. }
  158. // highlights enhancing
  159. private _createHighlightsPostProcess(ratio: number): void {
  160. this._highlightsPostProcess = new PostProcess("LensHighlights", "lensHighlights",
  161. [ "pentagon", "gain", "threshold", "screen_width", "screen_height" ], // uniforms
  162. [], // samplers
  163. ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
  164. this._scene.getEngine(), false);
  165. this._highlightsPostProcess.onApply = (effect: Effect) => {
  166. effect.setFloat('gain', this._highlightsGain);
  167. effect.setFloat('threshold', this._highlightsThreshold);
  168. effect.setBool('pentagon', this._dofPentagon);
  169. effect.setTextureFromPostProcess("textureSampler", this._chromaticAberrationPostProcess);
  170. effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
  171. effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
  172. };
  173. }
  174. // colors shifting and distortion
  175. private _createDepthOfFieldPostProcess(ratio: number): void {
  176. this._depthOfFieldPostProcess = new PostProcess("LensDepthOfField", "depthOfField",
  177. [
  178. "focus_depth", "aperture", "pentagon", "maxZ", "edge_blur", "chromatic_aberration",
  179. "distortion", "blur_noise", "grain_amount", "screen_width", "screen_height", "highlights"
  180. ],
  181. ["depthSampler", "grainSampler", "highlightsSampler"],
  182. ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
  183. this._scene.getEngine(), false);
  184. this._depthOfFieldPostProcess.onApply = (effect: Effect) => {
  185. effect.setBool('blur_noise', this._blurNoise);
  186. effect.setFloat('maxZ', this._scene.activeCamera.maxZ);
  187. effect.setFloat('grain_amount', this._grainAmount);
  188. effect.setTexture("depthSampler", this._depthTexture);
  189. effect.setTexture("grainSampler", this._grainTexture);
  190. effect.setTextureFromPostProcess("textureSampler", this._highlightsPostProcess);
  191. effect.setTextureFromPostProcess("highlightsSampler", this._depthOfFieldPostProcess);
  192. effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
  193. effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
  194. effect.setFloat('distortion', this._distortion);
  195. effect.setFloat('focus_depth', this._dofDepth);
  196. effect.setFloat('aperture', this._dofAperture);
  197. effect.setFloat('edge_blur', this._edgeBlur);
  198. effect.setBool('highlights', (this._highlightsGain != -1));
  199. };
  200. }
  201. // creates a black and white random noise texture, 512x512
  202. private _createGrainTexture(): void {
  203. var size = 512;
  204. this._grainTexture = new DynamicTexture("LensNoiseTexture", size, this._scene, false, Texture.BILINEAR_SAMPLINGMODE);
  205. this._grainTexture.wrapU = Texture.WRAP_ADDRESSMODE;
  206. this._grainTexture.wrapV = Texture.WRAP_ADDRESSMODE;
  207. var context = (<DynamicTexture>this._grainTexture).getContext();
  208. var rand = (min, max) => {
  209. return Math.random() * (max - min) + min;
  210. }
  211. var value;
  212. for (var x = 0; x < size; x++) {
  213. for (var y = 0; y < size; y++) {
  214. value = Math.floor(rand(0.42,0.58)*255);
  215. context.fillStyle = 'rgb(' + value + ', ' + value + ', ' + value + ')';
  216. context.fillRect(x, y, 1, 1);
  217. }
  218. }
  219. (<DynamicTexture>this._grainTexture).update(false);
  220. }
  221. }
  222. }