babylon.lensRenderingPipeline.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. module BABYLON {
  2. /**
  3. * BABYLON.JS Chromatic Aberration GLSL Shader
  4. * Author: Olivier Guyot
  5. * Separates very slightly R, G and B colors on the edges of the screen
  6. * Inspired by Francois Tarlier & Martins Upitis
  7. */
  8. export class LensRenderingPipeline extends PostProcessRenderPipeline {
  9. // Lens effects can be of the following:
  10. // - chromatic aberration (slight shift of RGB colors)
  11. // - blur on the edge of the lens
  12. // - lens distortion
  13. // - depth-of-field blur & highlights enhancing
  14. // - depth-of-field 'bokeh' effect (shapes appearing in blurred areas)
  15. // - grain effect (noise or custom texture)
  16. // Two additional texture samplers are needed:
  17. // - depth map (for depth-of-field)
  18. // - grain texture
  19. /**
  20. * @ignore
  21. * The chromatic aberration PostProcess id in the pipeline
  22. */
  23. public LensChromaticAberrationEffect: string = "LensChromaticAberrationEffect";
  24. /**
  25. * @ignore
  26. * The highlights enhancing PostProcess id in the pipeline
  27. */
  28. public HighlightsEnhancingEffect: string = "HighlightsEnhancingEffect";
  29. /**
  30. * @ignore
  31. * The depth-of-field PostProcess id in the pipeline
  32. */
  33. public LensDepthOfFieldEffect: string = "LensDepthOfFieldEffect";
  34. private _scene: Scene;
  35. private _depthTexture: RenderTargetTexture;
  36. private _grainTexture: Texture;
  37. private _chromaticAberrationPostProcess: PostProcess;
  38. private _highlightsPostProcess: PostProcess;
  39. private _depthOfFieldPostProcess: PostProcess;
  40. private _edgeBlur: number;
  41. private _grainAmount: number;
  42. private _chromaticAberration: number;
  43. private _distortion: number;
  44. private _highlightsGain: number;
  45. private _highlightsThreshold: number;
  46. private _dofDistance: number;
  47. private _dofAperture: number;
  48. private _dofDarken: number;
  49. private _dofPentagon: boolean;
  50. private _blurNoise: boolean;
  51. /**
  52. * @constructor
  53. *
  54. * Effect parameters are as follow:
  55. * {
  56. * chromatic_aberration: number; // from 0 to x (1 for realism)
  57. * edge_blur: number; // from 0 to x (1 for realism)
  58. * distortion: number; // from 0 to x (1 for realism)
  59. * grain_amount: number; // from 0 to 1
  60. * grain_texture: BABYLON.Texture; // texture to use for grain effect; if unset, use random B&W noise
  61. * dof_focus_distance: number; // depth-of-field: focus distance; unset to disable (disabled by default)
  62. * dof_aperture: number; // depth-of-field: focus blur bias (default: 1)
  63. * dof_darken: number; // depth-of-field: darken that which is out of focus (from 0 to 1, disabled by default)
  64. * dof_pentagon: boolean; // depth-of-field: makes a pentagon-like "bokeh" effect
  65. * dof_gain: number; // depth-of-field: highlights gain; unset to disable (disabled by default)
  66. * dof_threshold: number; // depth-of-field: highlights threshold (default: 1)
  67. * blur_noise: boolean; // add a little bit of noise to the blur (default: true)
  68. * }
  69. * Note: if an effect parameter is unset, effect is disabled
  70. *
  71. * @param {string} name - The rendering pipeline name
  72. * @param {object} parameters - An object containing all parameters (see above)
  73. * @param {BABYLON.Scene} scene - The scene linked to this pipeline
  74. * @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)
  75. * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
  76. */
  77. constructor(name: string, parameters: any, scene: Scene, ratio: number = 1.0, cameras?: Camera[]) {
  78. super(scene.getEngine(), name);
  79. this._scene = scene;
  80. // Fetch texture samplers
  81. this._depthTexture = scene.enableDepthRenderer().getDepthMap(); // Force depth renderer "on"
  82. if (parameters.grain_texture) { this._grainTexture = parameters.grain_texture; }
  83. else { this._createGrainTexture(); }
  84. // save parameters
  85. this._edgeBlur = parameters.edge_blur ? parameters.edge_blur : 0;
  86. this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
  87. this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
  88. this._distortion = parameters.distortion ? parameters.distortion : 0;
  89. this._highlightsGain = parameters.dof_gain !== undefined ? parameters.dof_gain : -1;
  90. this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
  91. this._dofDistance = parameters.dof_focus_distance !== undefined ? parameters.dof_focus_distance : -1;
  92. this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
  93. this._dofDarken = parameters.dof_darken ? parameters.dof_darken : 0;
  94. this._dofPentagon = parameters.dof_pentagon !== undefined ? parameters.dof_pentagon : true;
  95. this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
  96. // Create effects
  97. this._createChromaticAberrationPostProcess(ratio);
  98. this._createHighlightsPostProcess(ratio);
  99. this._createDepthOfFieldPostProcess(ratio / 4);
  100. // Set up pipeline
  101. this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensChromaticAberrationEffect, () => { return this._chromaticAberrationPostProcess; }, true));
  102. this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.HighlightsEnhancingEffect, () => { return this._highlightsPostProcess; }, true));
  103. this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensDepthOfFieldEffect, () => { return this._depthOfFieldPostProcess; }, true));
  104. if (this._highlightsGain === -1) {
  105. this._disableEffect(this.HighlightsEnhancingEffect, null);
  106. }
  107. // Finish
  108. scene.postProcessRenderPipelineManager.addPipeline(this);
  109. if (cameras) {
  110. scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
  111. }
  112. }
  113. // public methods (self explanatory)
  114. /**
  115. * Sets the amount of blur at the edges
  116. * @param amount blur amount
  117. */
  118. public setEdgeBlur(amount: number) { this._edgeBlur = amount; }
  119. /**
  120. * Sets edge blur to 0
  121. */
  122. public disableEdgeBlur() { this._edgeBlur = 0; }
  123. /**
  124. * Sets the amout of grain
  125. * @param amount Amount of grain
  126. */
  127. public setGrainAmount(amount: number) { this._grainAmount = amount; }
  128. /**
  129. * Set grain amount to 0
  130. */
  131. public disableGrain() { this._grainAmount = 0; }
  132. /**
  133. * Sets the chromatic aberration amount
  134. * @param amount amount of chromatic aberration
  135. */
  136. public setChromaticAberration(amount: number) { this._chromaticAberration = amount; }
  137. /**
  138. * Sets chromatic aberration amount to 0
  139. */
  140. public disableChromaticAberration() { this._chromaticAberration = 0; }
  141. /**
  142. * Sets the EdgeDistortion amount
  143. * @param amount amount of EdgeDistortion
  144. */
  145. public setEdgeDistortion(amount: number) { this._distortion = amount; }
  146. /**
  147. * Sets edge distortion to 0
  148. */
  149. public disableEdgeDistortion() { this._distortion = 0; }
  150. /**
  151. * Sets the FocusDistance amount
  152. * @param amount amount of FocusDistance
  153. */
  154. public setFocusDistance(amount: number) { this._dofDistance = amount; }
  155. /**
  156. * Disables depth of field
  157. */
  158. public disableDepthOfField() { this._dofDistance = -1; }
  159. /**
  160. * Sets the Aperture amount
  161. * @param amount amount of Aperture
  162. */
  163. public setAperture(amount: number) { this._dofAperture = amount; }
  164. /**
  165. * Sets the DarkenOutOfFocus amount
  166. * @param amount amount of DarkenOutOfFocus
  167. */
  168. public setDarkenOutOfFocus(amount: number) { this._dofDarken = amount; }
  169. /**
  170. * Creates a pentagon bokeh effect
  171. */
  172. public enablePentagonBokeh() {
  173. this._highlightsPostProcess.updateEffect("#define PENTAGON\n");
  174. }
  175. /**
  176. * Disables the pentagon bokeh effect
  177. */
  178. public disablePentagonBokeh() {
  179. this._highlightsPostProcess.updateEffect();
  180. }
  181. /**
  182. * Enables noise blur
  183. */
  184. public enableNoiseBlur() { this._blurNoise = true; }
  185. /**
  186. * Disables noise blur
  187. */
  188. public disableNoiseBlur() { this._blurNoise = false; }
  189. /**
  190. * Sets the HighlightsGain amount
  191. * @param amount amount of HighlightsGain
  192. */
  193. public setHighlightsGain(amount: number) {
  194. this._highlightsGain = amount;
  195. }
  196. /**
  197. * Sets the HighlightsThreshold amount
  198. * @param amount amount of HighlightsThreshold
  199. */
  200. public setHighlightsThreshold(amount: number) {
  201. if (this._highlightsGain === -1) {
  202. this._highlightsGain = 1.0;
  203. }
  204. this._highlightsThreshold = amount;
  205. }
  206. /**
  207. * Disables highlights
  208. */
  209. public disableHighlights() {
  210. this._highlightsGain = -1;
  211. }
  212. /**
  213. * Removes the internal pipeline assets and detaches the pipeline from the scene cameras
  214. * @param disableDepthRender If the scens depth rendering should be disabled (default: false)
  215. */
  216. public dispose(disableDepthRender: boolean = false): void {
  217. this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
  218. (<any>this._chromaticAberrationPostProcess) = null;
  219. (<any>this._highlightsPostProcess) = null;
  220. (<any>this._depthOfFieldPostProcess) = null;
  221. this._grainTexture.dispose();
  222. if (disableDepthRender)
  223. this._scene.disableDepthRenderer();
  224. }
  225. // colors shifting and distortion
  226. private _createChromaticAberrationPostProcess(ratio: number): void {
  227. this._chromaticAberrationPostProcess = new PostProcess("LensChromaticAberration", "chromaticAberration",
  228. ["chromatic_aberration", "screen_width", "screen_height", "direction", "radialIntensity", "centerPosition"], // uniforms
  229. [], // samplers
  230. ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
  231. this._scene.getEngine(), false);
  232. this._chromaticAberrationPostProcess.onApply = (effect: Effect) => {
  233. effect.setFloat('chromatic_aberration', this._chromaticAberration);
  234. effect.setFloat('screen_width', this._scene.getEngine().getRenderWidth());
  235. effect.setFloat('screen_height', this._scene.getEngine().getRenderHeight());
  236. effect.setFloat('radialIntensity', 1);
  237. effect.setFloat2('direction', 17, 17);
  238. effect.setFloat2('centerPosition', 0.5,0.5);
  239. };
  240. }
  241. // highlights enhancing
  242. private _createHighlightsPostProcess(ratio: number): void {
  243. this._highlightsPostProcess = new PostProcess("LensHighlights", "lensHighlights",
  244. ["gain", "threshold", "screen_width", "screen_height"], // uniforms
  245. [], // samplers
  246. ratio,
  247. null, Texture.TRILINEAR_SAMPLINGMODE,
  248. this._scene.getEngine(), false, this._dofPentagon ? "#define PENTAGON\n" : "");
  249. this._highlightsPostProcess.onApply = (effect: Effect) => {
  250. effect.setFloat('gain', this._highlightsGain);
  251. effect.setFloat('threshold', this._highlightsThreshold);
  252. effect.setTextureFromPostProcess("textureSampler", this._chromaticAberrationPostProcess);
  253. effect.setFloat('screen_width', this._scene.getEngine().getRenderWidth());
  254. effect.setFloat('screen_height', this._scene.getEngine().getRenderHeight());
  255. };
  256. }
  257. // colors shifting and distortion
  258. private _createDepthOfFieldPostProcess(ratio: number): void {
  259. this._depthOfFieldPostProcess = new PostProcess("LensDepthOfField", "depthOfField",
  260. [
  261. "grain_amount", "blur_noise", "screen_width", "screen_height", "distortion", "dof_enabled",
  262. "screen_distance", "aperture", "darken", "edge_blur", "highlights", "near", "far"
  263. ],
  264. ["depthSampler", "grainSampler", "highlightsSampler"],
  265. ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
  266. this._scene.getEngine(), false);
  267. this._depthOfFieldPostProcess.onApply = (effect: Effect) => {
  268. effect.setTexture("depthSampler", this._depthTexture);
  269. effect.setTexture("grainSampler", this._grainTexture);
  270. effect.setTextureFromPostProcess("textureSampler", this._highlightsPostProcess);
  271. effect.setTextureFromPostProcess("highlightsSampler", this._depthOfFieldPostProcess);
  272. effect.setFloat('grain_amount', this._grainAmount);
  273. effect.setBool('blur_noise', this._blurNoise);
  274. effect.setFloat('screen_width', this._scene.getEngine().getRenderWidth());
  275. effect.setFloat('screen_height', this._scene.getEngine().getRenderHeight());
  276. effect.setFloat('distortion', this._distortion);
  277. effect.setBool('dof_enabled', (this._dofDistance !== -1));
  278. effect.setFloat('screen_distance', 1.0 / (0.1 - 1.0 / this._dofDistance));
  279. effect.setFloat('aperture', this._dofAperture);
  280. effect.setFloat('darken', this._dofDarken);
  281. effect.setFloat('edge_blur', this._edgeBlur);
  282. effect.setBool('highlights', (this._highlightsGain !== -1));
  283. if (this._scene.activeCamera) {
  284. effect.setFloat('near', this._scene.activeCamera.minZ);
  285. effect.setFloat('far', this._scene.activeCamera.maxZ);
  286. }
  287. };
  288. }
  289. // creates a black and white random noise texture, 512x512
  290. private _createGrainTexture(): void {
  291. var size = 512;
  292. this._grainTexture = new DynamicTexture("LensNoiseTexture", size, this._scene, false, Texture.BILINEAR_SAMPLINGMODE);
  293. this._grainTexture.wrapU = Texture.WRAP_ADDRESSMODE;
  294. this._grainTexture.wrapV = Texture.WRAP_ADDRESSMODE;
  295. var context = (<DynamicTexture>this._grainTexture).getContext();
  296. var rand = (min: number, max: number) => {
  297. return Math.random() * (max - min) + min;
  298. }
  299. var value;
  300. for (var x = 0; x < size; x++) {
  301. for (var y = 0; y < size; y++) {
  302. value = Math.floor(rand(0.42, 0.58) * 255);
  303. context.fillStyle = 'rgb(' + value + ', ' + value + ', ' + value + ')';
  304. context.fillRect(x, y, 1, 1);
  305. }
  306. }
  307. (<DynamicTexture>this._grainTexture).update(false);
  308. }
  309. }
  310. }