RenderPixelatedPass.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import {
  2. WebGLRenderTarget,
  3. MeshNormalMaterial,
  4. ShaderMaterial,
  5. Vector2,
  6. Vector4,
  7. DepthTexture,
  8. NearestFilter,
  9. HalfFloatType
  10. } from 'three';
  11. import { Pass, FullScreenQuad } from './Pass.js';
  12. /**
  13. * A special type of render pass that produces a pixelated beauty pass.
  14. *
  15. * ```js
  16. * const renderPixelatedPass = new RenderPixelatedPass( 6, scene, camera );
  17. * composer.addPass( renderPixelatedPass );
  18. * ```
  19. *
  20. * @augments Pass
  21. */
  22. class RenderPixelatedPass extends Pass {
  23. /**
  24. * Constructs a new render pixelated pass.
  25. *
  26. * @param {number} pixelSize - The effect's pixel size.
  27. * @param {Scene} scene - The scene to render.
  28. * @param {Camera} camera - The camera.
  29. * @param {{normalEdgeStrength:number,depthEdgeStrength:number}} options - The pass options.
  30. */
  31. constructor( pixelSize, scene, camera, options = {} ) {
  32. super();
  33. /**
  34. * The effect's pixel size.
  35. *
  36. * @type {number}
  37. */
  38. this.pixelSize = pixelSize;
  39. /**
  40. * The scene to render.
  41. *
  42. * @type {Scene}
  43. */
  44. this.scene = scene;
  45. /**
  46. * The camera.
  47. *
  48. * @type {Camera}
  49. */
  50. this.camera = camera;
  51. /**
  52. * The normal edge strength.
  53. *
  54. * @type {number}
  55. * @default 0.3
  56. */
  57. this.normalEdgeStrength = options.normalEdgeStrength || 0.3;
  58. /**
  59. * The normal edge strength.
  60. *
  61. * @type {number}
  62. * @default 0.4
  63. */
  64. this.depthEdgeStrength = options.depthEdgeStrength || 0.4;
  65. /**
  66. * The pixelated material.
  67. *
  68. * @type {ShaderMaterial}
  69. */
  70. this.pixelatedMaterial = this._createPixelatedMaterial();
  71. // internals
  72. this._resolution = new Vector2();
  73. this._renderResolution = new Vector2();
  74. this._normalMaterial = new MeshNormalMaterial();
  75. this._beautyRenderTarget = new WebGLRenderTarget();
  76. this._beautyRenderTarget.texture.minFilter = NearestFilter;
  77. this._beautyRenderTarget.texture.magFilter = NearestFilter;
  78. this._beautyRenderTarget.texture.type = HalfFloatType;
  79. this._beautyRenderTarget.depthTexture = new DepthTexture();
  80. this._normalRenderTarget = new WebGLRenderTarget();
  81. this._normalRenderTarget.texture.minFilter = NearestFilter;
  82. this._normalRenderTarget.texture.magFilter = NearestFilter;
  83. this._normalRenderTarget.texture.type = HalfFloatType;
  84. this._fsQuad = new FullScreenQuad( this.pixelatedMaterial );
  85. }
  86. /**
  87. * Frees the GPU-related resources allocated by this instance. Call this
  88. * method whenever the pass is no longer used in your app.
  89. */
  90. dispose() {
  91. this._beautyRenderTarget.dispose();
  92. this._normalRenderTarget.dispose();
  93. this.pixelatedMaterial.dispose();
  94. this._normalMaterial.dispose();
  95. this._fsQuad.dispose();
  96. }
  97. /**
  98. * Sets the size of the pass.
  99. *
  100. * @param {number} width - The width to set.
  101. * @param {number} height - The width to set.
  102. */
  103. setSize( width, height ) {
  104. this._resolution.set( width, height );
  105. this._renderResolution.set( ( width / this.pixelSize ) | 0, ( height / this.pixelSize ) | 0 );
  106. const { x, y } = this._renderResolution;
  107. this._beautyRenderTarget.setSize( x, y );
  108. this._normalRenderTarget.setSize( x, y );
  109. this._fsQuad.material.uniforms.resolution.value.set( x, y, 1 / x, 1 / y );
  110. }
  111. /**
  112. * Sets the effect's pixel size.
  113. *
  114. * @param {number} pixelSize - The pixel size to set.
  115. */
  116. setPixelSize( pixelSize ) {
  117. this.pixelSize = pixelSize;
  118. this.setSize( this._resolution.x, this._resolution.y );
  119. }
  120. /**
  121. * Performs the pixelation pass.
  122. *
  123. * @param {WebGLRenderer} renderer - The renderer.
  124. * @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
  125. * destination for the pass.
  126. * @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
  127. * previous pass from this buffer.
  128. * @param {number} deltaTime - The delta time in seconds.
  129. * @param {boolean} maskActive - Whether masking is active or not.
  130. */
  131. render( renderer, writeBuffer/*, readBuffer , deltaTime, maskActive */ ) {
  132. const uniforms = this._fsQuad.material.uniforms;
  133. uniforms.normalEdgeStrength.value = this.normalEdgeStrength;
  134. uniforms.depthEdgeStrength.value = this.depthEdgeStrength;
  135. renderer.setRenderTarget( this._beautyRenderTarget );
  136. renderer.render( this.scene, this.camera );
  137. const overrideMaterial_old = this.scene.overrideMaterial;
  138. renderer.setRenderTarget( this._normalRenderTarget );
  139. this.scene.overrideMaterial = this._normalMaterial;
  140. renderer.render( this.scene, this.camera );
  141. this.scene.overrideMaterial = overrideMaterial_old;
  142. uniforms.tDiffuse.value = this._beautyRenderTarget.texture;
  143. uniforms.tDepth.value = this._beautyRenderTarget.depthTexture;
  144. uniforms.tNormal.value = this._normalRenderTarget.texture;
  145. if ( this.renderToScreen ) {
  146. renderer.setRenderTarget( null );
  147. } else {
  148. renderer.setRenderTarget( writeBuffer );
  149. if ( this.clear ) renderer.clear();
  150. }
  151. this._fsQuad.render( renderer );
  152. }
  153. // internals
  154. _createPixelatedMaterial() {
  155. return new ShaderMaterial( {
  156. uniforms: {
  157. tDiffuse: { value: null },
  158. tDepth: { value: null },
  159. tNormal: { value: null },
  160. resolution: { value: new Vector4() },
  161. normalEdgeStrength: { value: 0 },
  162. depthEdgeStrength: { value: 0 }
  163. },
  164. vertexShader: /* glsl */`
  165. varying vec2 vUv;
  166. void main() {
  167. vUv = uv;
  168. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  169. }
  170. `,
  171. fragmentShader: /* glsl */`
  172. uniform sampler2D tDiffuse;
  173. uniform sampler2D tDepth;
  174. uniform sampler2D tNormal;
  175. uniform vec4 resolution;
  176. uniform float normalEdgeStrength;
  177. uniform float depthEdgeStrength;
  178. varying vec2 vUv;
  179. float getDepth(int x, int y) {
  180. return texture2D( tDepth, vUv + vec2(x, y) * resolution.zw ).r;
  181. }
  182. vec3 getNormal(int x, int y) {
  183. return texture2D( tNormal, vUv + vec2(x, y) * resolution.zw ).rgb * 2.0 - 1.0;
  184. }
  185. float depthEdgeIndicator(float depth, vec3 normal) {
  186. float diff = 0.0;
  187. diff += clamp(getDepth(1, 0) - depth, 0.0, 1.0);
  188. diff += clamp(getDepth(-1, 0) - depth, 0.0, 1.0);
  189. diff += clamp(getDepth(0, 1) - depth, 0.0, 1.0);
  190. diff += clamp(getDepth(0, -1) - depth, 0.0, 1.0);
  191. return floor(smoothstep(0.01, 0.02, diff) * 2.) / 2.;
  192. }
  193. float neighborNormalEdgeIndicator(int x, int y, float depth, vec3 normal) {
  194. float depthDiff = getDepth(x, y) - depth;
  195. vec3 neighborNormal = getNormal(x, y);
  196. // Edge pixels should yield to faces who's normals are closer to the bias normal.
  197. vec3 normalEdgeBias = vec3(1., 1., 1.); // This should probably be a parameter.
  198. float normalDiff = dot(normal - neighborNormal, normalEdgeBias);
  199. float normalIndicator = clamp(smoothstep(-.01, .01, normalDiff), 0.0, 1.0);
  200. // Only the shallower pixel should detect the normal edge.
  201. float depthIndicator = clamp(sign(depthDiff * .25 + .0025), 0.0, 1.0);
  202. return (1.0 - dot(normal, neighborNormal)) * depthIndicator * normalIndicator;
  203. }
  204. float normalEdgeIndicator(float depth, vec3 normal) {
  205. float indicator = 0.0;
  206. indicator += neighborNormalEdgeIndicator(0, -1, depth, normal);
  207. indicator += neighborNormalEdgeIndicator(0, 1, depth, normal);
  208. indicator += neighborNormalEdgeIndicator(-1, 0, depth, normal);
  209. indicator += neighborNormalEdgeIndicator(1, 0, depth, normal);
  210. return step(0.1, indicator);
  211. }
  212. void main() {
  213. vec4 texel = texture2D( tDiffuse, vUv );
  214. float depth = 0.0;
  215. vec3 normal = vec3(0.0);
  216. if (depthEdgeStrength > 0.0 || normalEdgeStrength > 0.0) {
  217. depth = getDepth(0, 0);
  218. normal = getNormal(0, 0);
  219. }
  220. float dei = 0.0;
  221. if (depthEdgeStrength > 0.0)
  222. dei = depthEdgeIndicator(depth, normal);
  223. float nei = 0.0;
  224. if (normalEdgeStrength > 0.0)
  225. nei = normalEdgeIndicator(depth, normal);
  226. float Strength = dei > 0.0 ? (1.0 - depthEdgeStrength * dei) : (1.0 + normalEdgeStrength * nei);
  227. gl_FragColor = texel * Strength;
  228. }
  229. `
  230. } );
  231. }
  232. }
  233. export { RenderPixelatedPass };
粤ICP备19079148号