2
0

RenderPixelatedPass.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. class RenderPixelatedPass extends Pass {
  13. constructor( pixelSize, scene, camera, options = {} ) {
  14. super();
  15. this.pixelSize = pixelSize;
  16. this.resolution = new Vector2();
  17. this.renderResolution = new Vector2();
  18. this.pixelatedMaterial = this.createPixelatedMaterial();
  19. this.normalMaterial = new MeshNormalMaterial();
  20. this.fsQuad = new FullScreenQuad( this.pixelatedMaterial );
  21. this.scene = scene;
  22. this.camera = camera;
  23. this.normalEdgeStrength = options.normalEdgeStrength || 0.3;
  24. this.depthEdgeStrength = options.depthEdgeStrength || 0.4;
  25. this.beautyRenderTarget = new WebGLRenderTarget();
  26. this.beautyRenderTarget.texture.minFilter = NearestFilter;
  27. this.beautyRenderTarget.texture.magFilter = NearestFilter;
  28. this.beautyRenderTarget.texture.type = HalfFloatType;
  29. this.beautyRenderTarget.depthTexture = new DepthTexture();
  30. this.normalRenderTarget = new WebGLRenderTarget();
  31. this.normalRenderTarget.texture.minFilter = NearestFilter;
  32. this.normalRenderTarget.texture.magFilter = NearestFilter;
  33. this.normalRenderTarget.texture.type = HalfFloatType;
  34. }
  35. dispose() {
  36. this.beautyRenderTarget.dispose();
  37. this.normalRenderTarget.dispose();
  38. this.pixelatedMaterial.dispose();
  39. this.normalMaterial.dispose();
  40. this.fsQuad.dispose();
  41. }
  42. setSize( width, height ) {
  43. this.resolution.set( width, height );
  44. this.renderResolution.set( ( width / this.pixelSize ) | 0, ( height / this.pixelSize ) | 0 );
  45. const { x, y } = this.renderResolution;
  46. this.beautyRenderTarget.setSize( x, y );
  47. this.normalRenderTarget.setSize( x, y );
  48. this.fsQuad.material.uniforms.resolution.value.set( x, y, 1 / x, 1 / y );
  49. }
  50. setPixelSize( pixelSize ) {
  51. this.pixelSize = pixelSize;
  52. this.setSize( this.resolution.x, this.resolution.y );
  53. }
  54. render( renderer, writeBuffer ) {
  55. const uniforms = this.fsQuad.material.uniforms;
  56. uniforms.normalEdgeStrength.value = this.normalEdgeStrength;
  57. uniforms.depthEdgeStrength.value = this.depthEdgeStrength;
  58. renderer.setRenderTarget( this.beautyRenderTarget );
  59. renderer.render( this.scene, this.camera );
  60. const overrideMaterial_old = this.scene.overrideMaterial;
  61. renderer.setRenderTarget( this.normalRenderTarget );
  62. this.scene.overrideMaterial = this.normalMaterial;
  63. renderer.render( this.scene, this.camera );
  64. this.scene.overrideMaterial = overrideMaterial_old;
  65. uniforms.tDiffuse.value = this.beautyRenderTarget.texture;
  66. uniforms.tDepth.value = this.beautyRenderTarget.depthTexture;
  67. uniforms.tNormal.value = this.normalRenderTarget.texture;
  68. if ( this.renderToScreen ) {
  69. renderer.setRenderTarget( null );
  70. } else {
  71. renderer.setRenderTarget( writeBuffer );
  72. if ( this.clear ) renderer.clear();
  73. }
  74. this.fsQuad.render( renderer );
  75. }
  76. createPixelatedMaterial() {
  77. return new ShaderMaterial( {
  78. uniforms: {
  79. tDiffuse: { value: null },
  80. tDepth: { value: null },
  81. tNormal: { value: null },
  82. resolution: {
  83. value: new Vector4(
  84. this.renderResolution.x,
  85. this.renderResolution.y,
  86. 1 / this.renderResolution.x,
  87. 1 / this.renderResolution.y,
  88. )
  89. },
  90. normalEdgeStrength: { value: 0 },
  91. depthEdgeStrength: { value: 0 }
  92. },
  93. vertexShader: /* glsl */`
  94. varying vec2 vUv;
  95. void main() {
  96. vUv = uv;
  97. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  98. }
  99. `,
  100. fragmentShader: /* glsl */`
  101. uniform sampler2D tDiffuse;
  102. uniform sampler2D tDepth;
  103. uniform sampler2D tNormal;
  104. uniform vec4 resolution;
  105. uniform float normalEdgeStrength;
  106. uniform float depthEdgeStrength;
  107. varying vec2 vUv;
  108. float getDepth(int x, int y) {
  109. return texture2D( tDepth, vUv + vec2(x, y) * resolution.zw ).r;
  110. }
  111. vec3 getNormal(int x, int y) {
  112. return texture2D( tNormal, vUv + vec2(x, y) * resolution.zw ).rgb * 2.0 - 1.0;
  113. }
  114. float depthEdgeIndicator(float depth, vec3 normal) {
  115. float diff = 0.0;
  116. diff += clamp(getDepth(1, 0) - depth, 0.0, 1.0);
  117. diff += clamp(getDepth(-1, 0) - depth, 0.0, 1.0);
  118. diff += clamp(getDepth(0, 1) - depth, 0.0, 1.0);
  119. diff += clamp(getDepth(0, -1) - depth, 0.0, 1.0);
  120. return floor(smoothstep(0.01, 0.02, diff) * 2.) / 2.;
  121. }
  122. float neighborNormalEdgeIndicator(int x, int y, float depth, vec3 normal) {
  123. float depthDiff = getDepth(x, y) - depth;
  124. vec3 neighborNormal = getNormal(x, y);
  125. // Edge pixels should yield to faces who's normals are closer to the bias normal.
  126. vec3 normalEdgeBias = vec3(1., 1., 1.); // This should probably be a parameter.
  127. float normalDiff = dot(normal - neighborNormal, normalEdgeBias);
  128. float normalIndicator = clamp(smoothstep(-.01, .01, normalDiff), 0.0, 1.0);
  129. // Only the shallower pixel should detect the normal edge.
  130. float depthIndicator = clamp(sign(depthDiff * .25 + .0025), 0.0, 1.0);
  131. return (1.0 - dot(normal, neighborNormal)) * depthIndicator * normalIndicator;
  132. }
  133. float normalEdgeIndicator(float depth, vec3 normal) {
  134. float indicator = 0.0;
  135. indicator += neighborNormalEdgeIndicator(0, -1, depth, normal);
  136. indicator += neighborNormalEdgeIndicator(0, 1, depth, normal);
  137. indicator += neighborNormalEdgeIndicator(-1, 0, depth, normal);
  138. indicator += neighborNormalEdgeIndicator(1, 0, depth, normal);
  139. return step(0.1, indicator);
  140. }
  141. void main() {
  142. vec4 texel = texture2D( tDiffuse, vUv );
  143. float depth = 0.0;
  144. vec3 normal = vec3(0.0);
  145. if (depthEdgeStrength > 0.0 || normalEdgeStrength > 0.0) {
  146. depth = getDepth(0, 0);
  147. normal = getNormal(0, 0);
  148. }
  149. float dei = 0.0;
  150. if (depthEdgeStrength > 0.0)
  151. dei = depthEdgeIndicator(depth, normal);
  152. float nei = 0.0;
  153. if (normalEdgeStrength > 0.0)
  154. nei = normalEdgeIndicator(depth, normal);
  155. float Strength = dei > 0.0 ? (1.0 - depthEdgeStrength * dei) : (1.0 + normalEdgeStrength * nei);
  156. gl_FragColor = texel * Strength;
  157. }
  158. `
  159. } );
  160. }
  161. }
  162. export { RenderPixelatedPass };