GTAOShader.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. import {
  2. DataTexture,
  3. Matrix4,
  4. RepeatWrapping,
  5. Vector2,
  6. Vector3,
  7. } from 'three';
  8. /**
  9. * @module GTAOShader
  10. * @three_import import { GTAOShader } from 'three/addons/shaders/GTAOShader.js';
  11. */
  12. /**
  13. * GTAO shader. Use by {@link GTAOPass}.
  14. *
  15. * References:
  16. * - [Practical Realtime Strategies for Accurate Indirect Occlusion](https://iryoku.com/downloads/Practical-Realtime-Strategies-for-Accurate-Indirect-Occlusion.pdf).
  17. * - [Horizon-Based Indirect Lighting (HBIL)](https://github.com/Patapom/GodComplex/blob/master/Tests/TestHBIL/2018%20Mayaux%20-%20Horizon-Based%20Indirect%20Lighting%20(HBIL).pdf)
  18. *
  19. * @constant
  20. * @type {ShaderMaterial~Shader}
  21. */
  22. const GTAOShader = {
  23. name: 'GTAOShader',
  24. defines: {
  25. PERSPECTIVE_CAMERA: 1,
  26. SAMPLES: 16,
  27. NORMAL_VECTOR_TYPE: 1,
  28. DEPTH_SWIZZLING: 'x',
  29. SCREEN_SPACE_RADIUS: 0,
  30. SCREEN_SPACE_RADIUS_SCALE: 100.0,
  31. SCENE_CLIP_BOX: 0,
  32. },
  33. uniforms: {
  34. tNormal: { value: null },
  35. tDepth: { value: null },
  36. tNoise: { value: null },
  37. resolution: { value: new Vector2() },
  38. cameraNear: { value: null },
  39. cameraFar: { value: null },
  40. cameraProjectionMatrix: { value: new Matrix4() },
  41. cameraProjectionMatrixInverse: { value: new Matrix4() },
  42. cameraWorldMatrix: { value: new Matrix4() },
  43. radius: { value: 0.25 },
  44. distanceExponent: { value: 1. },
  45. thickness: { value: 1. },
  46. distanceFallOff: { value: 1. },
  47. scale: { value: 1. },
  48. sceneBoxMin: { value: new Vector3( - 1, - 1, - 1 ) },
  49. sceneBoxMax: { value: new Vector3( 1, 1, 1 ) },
  50. },
  51. vertexShader: /* glsl */`
  52. varying vec2 vUv;
  53. void main() {
  54. vUv = uv;
  55. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  56. }`,
  57. fragmentShader: /* glsl */`
  58. varying vec2 vUv;
  59. uniform highp sampler2D tNormal;
  60. uniform highp sampler2D tDepth;
  61. uniform sampler2D tNoise;
  62. uniform vec2 resolution;
  63. uniform float cameraNear;
  64. uniform float cameraFar;
  65. uniform mat4 cameraProjectionMatrix;
  66. uniform mat4 cameraProjectionMatrixInverse;
  67. uniform mat4 cameraWorldMatrix;
  68. uniform float radius;
  69. uniform float distanceExponent;
  70. uniform float thickness;
  71. uniform float distanceFallOff;
  72. uniform float scale;
  73. #if SCENE_CLIP_BOX == 1
  74. uniform vec3 sceneBoxMin;
  75. uniform vec3 sceneBoxMax;
  76. #endif
  77. #include <common>
  78. #include <packing>
  79. #ifndef FRAGMENT_OUTPUT
  80. #define FRAGMENT_OUTPUT vec4(vec3(ao), 1.)
  81. #endif
  82. vec3 getViewPosition( const in vec2 screenPosition, const in float depth ) {
  83. #ifdef USE_REVERSED_DEPTH_BUFFER
  84. vec4 clipSpacePosition = vec4( vec2( screenPosition ) * 2.0 - 1.0, depth, 1.0 );
  85. #else
  86. vec4 clipSpacePosition = vec4( vec3( screenPosition, depth ) * 2.0 - 1.0, 1.0 );
  87. #endif
  88. vec4 viewSpacePosition = cameraProjectionMatrixInverse * clipSpacePosition;
  89. return viewSpacePosition.xyz / viewSpacePosition.w;
  90. }
  91. float getDepth(const vec2 uv) {
  92. return textureLod(tDepth, uv.xy, 0.0).DEPTH_SWIZZLING;
  93. }
  94. float fetchDepth(const ivec2 uv) {
  95. return texelFetch(tDepth, uv.xy, 0).DEPTH_SWIZZLING;
  96. }
  97. float getViewZ(const in float depth) {
  98. #if PERSPECTIVE_CAMERA == 1
  99. return perspectiveDepthToViewZ(depth, cameraNear, cameraFar);
  100. #else
  101. return orthographicDepthToViewZ(depth, cameraNear, cameraFar);
  102. #endif
  103. }
  104. vec3 computeNormalFromDepth(const vec2 uv) {
  105. vec2 size = vec2(textureSize(tDepth, 0));
  106. ivec2 p = ivec2(uv * size);
  107. float c0 = fetchDepth(p);
  108. float l2 = fetchDepth(p - ivec2(2, 0));
  109. float l1 = fetchDepth(p - ivec2(1, 0));
  110. float r1 = fetchDepth(p + ivec2(1, 0));
  111. float r2 = fetchDepth(p + ivec2(2, 0));
  112. float b2 = fetchDepth(p - ivec2(0, 2));
  113. float b1 = fetchDepth(p - ivec2(0, 1));
  114. float t1 = fetchDepth(p + ivec2(0, 1));
  115. float t2 = fetchDepth(p + ivec2(0, 2));
  116. float dl = abs((2.0 * l1 - l2) - c0);
  117. float dr = abs((2.0 * r1 - r2) - c0);
  118. float db = abs((2.0 * b1 - b2) - c0);
  119. float dt = abs((2.0 * t1 - t2) - c0);
  120. vec3 ce = getViewPosition(uv, c0).xyz;
  121. vec3 dpdx = (dl < dr) ? ce - getViewPosition((uv - vec2(1.0 / size.x, 0.0)), l1).xyz : -ce + getViewPosition((uv + vec2(1.0 / size.x, 0.0)), r1).xyz;
  122. vec3 dpdy = (db < dt) ? ce - getViewPosition((uv - vec2(0.0, 1.0 / size.y)), b1).xyz : -ce + getViewPosition((uv + vec2(0.0, 1.0 / size.y)), t1).xyz;
  123. return normalize(cross(dpdx, dpdy));
  124. }
  125. vec3 getViewNormal(const vec2 uv) {
  126. #if NORMAL_VECTOR_TYPE == 2
  127. return normalize(textureLod(tNormal, uv, 0.).rgb);
  128. #elif NORMAL_VECTOR_TYPE == 1
  129. return unpackRGBToNormal(textureLod(tNormal, uv, 0.).rgb);
  130. #else
  131. return computeNormalFromDepth(uv);
  132. #endif
  133. }
  134. vec3 getSceneUvAndDepth(vec3 sampleViewPos) {
  135. vec4 sampleClipPos = cameraProjectionMatrix * vec4(sampleViewPos, 1.);
  136. vec2 sampleUv = sampleClipPos.xy / sampleClipPos.w * 0.5 + 0.5;
  137. float sampleSceneDepth = getDepth(sampleUv);
  138. return vec3(sampleUv, sampleSceneDepth);
  139. }
  140. void main() {
  141. float depth = getDepth(vUv.xy);
  142. #ifdef USE_REVERSED_DEPTH_BUFFER
  143. if (depth <= 0.0) {
  144. discard;
  145. return;
  146. }
  147. #else
  148. if (depth >= 1.0) {
  149. discard;
  150. return;
  151. }
  152. #endif
  153. vec3 viewPos = getViewPosition(vUv, depth);
  154. vec3 viewNormal = getViewNormal(vUv);
  155. float radiusToUse = radius;
  156. float distanceFalloffToUse = thickness;
  157. #if SCREEN_SPACE_RADIUS == 1
  158. float radiusScale = getViewPosition(vec2(0.5 + float(SCREEN_SPACE_RADIUS_SCALE) / resolution.x, 0.0), depth).x;
  159. radiusToUse *= radiusScale;
  160. distanceFalloffToUse *= radiusScale;
  161. #endif
  162. #if SCENE_CLIP_BOX == 1
  163. vec3 worldPos = (cameraWorldMatrix * vec4(viewPos, 1.0)).xyz;
  164. float boxDistance = length(max(vec3(0.0), max(sceneBoxMin - worldPos, worldPos - sceneBoxMax)));
  165. if (boxDistance > radiusToUse) {
  166. discard;
  167. return;
  168. }
  169. #endif
  170. vec2 noiseResolution = vec2(textureSize(tNoise, 0));
  171. vec2 noiseUv = vUv * resolution / noiseResolution;
  172. vec4 noiseTexel = textureLod(tNoise, noiseUv, 0.0);
  173. vec3 randomVec = noiseTexel.xyz * 2.0 - 1.0;
  174. vec3 tangent = normalize(vec3(randomVec.xy, 0.));
  175. vec3 bitangent = vec3(-tangent.y, tangent.x, 0.);
  176. mat3 kernelMatrix = mat3(tangent, bitangent, vec3(0., 0., 1.));
  177. const int DIRECTIONS = SAMPLES < 30 ? 3 : 5;
  178. const int STEPS = (SAMPLES + DIRECTIONS - 1) / DIRECTIONS;
  179. float ao = 0.0;
  180. for (int i = 0; i < DIRECTIONS; ++i) {
  181. float angle = float(i) / float(DIRECTIONS) * PI;
  182. vec4 sampleDir = vec4(cos(angle), sin(angle), 0., 0.5 + 0.5 * noiseTexel.w);
  183. sampleDir.xyz = normalize(kernelMatrix * sampleDir.xyz);
  184. vec3 viewDir = normalize(-viewPos.xyz);
  185. vec3 sliceBitangent = normalize(cross(sampleDir.xyz, viewDir));
  186. vec3 sliceTangent = cross(sliceBitangent, viewDir);
  187. vec3 normalInSlice = normalize(viewNormal - sliceBitangent * dot(viewNormal, sliceBitangent));
  188. vec3 tangentToNormalInSlice = cross(normalInSlice, sliceBitangent);
  189. vec2 cosHorizons = vec2(dot(viewDir, tangentToNormalInSlice), dot(viewDir, -tangentToNormalInSlice));
  190. for (int j = 0; j < STEPS; ++j) {
  191. vec3 sampleViewOffset = sampleDir.xyz * radiusToUse * sampleDir.w * pow(float(j + 1) / float(STEPS), distanceExponent);
  192. vec3 sampleSceneUvDepth = getSceneUvAndDepth(viewPos + sampleViewOffset);
  193. vec3 sampleSceneViewPos = getViewPosition(sampleSceneUvDepth.xy, sampleSceneUvDepth.z);
  194. vec3 viewDelta = sampleSceneViewPos - viewPos;
  195. if (abs(viewDelta.z) < thickness) {
  196. float sampleCosHorizon = dot(viewDir, normalize(viewDelta));
  197. cosHorizons.x += max(0., (sampleCosHorizon - cosHorizons.x) * mix(1., 2. / float(j + 2), distanceFallOff));
  198. }
  199. sampleSceneUvDepth = getSceneUvAndDepth(viewPos - sampleViewOffset);
  200. sampleSceneViewPos = getViewPosition(sampleSceneUvDepth.xy, sampleSceneUvDepth.z);
  201. viewDelta = sampleSceneViewPos - viewPos;
  202. if (abs(viewDelta.z) < thickness) {
  203. float sampleCosHorizon = dot(viewDir, normalize(viewDelta));
  204. cosHorizons.y += max(0., (sampleCosHorizon - cosHorizons.y) * mix(1., 2. / float(j + 2), distanceFallOff));
  205. }
  206. }
  207. vec2 sinHorizons = sqrt(1. - cosHorizons * cosHorizons);
  208. float nx = dot(normalInSlice, sliceTangent);
  209. float ny = dot(normalInSlice, viewDir);
  210. float nxb = 1. / 2. * (acos(cosHorizons.y) - acos(cosHorizons.x) + sinHorizons.x * cosHorizons.x - sinHorizons.y * cosHorizons.y);
  211. float nyb = 1. / 2. * (2. - cosHorizons.x * cosHorizons.x - cosHorizons.y * cosHorizons.y);
  212. float occlusion = nx * nxb + ny * nyb;
  213. ao += occlusion;
  214. }
  215. ao = clamp(ao / float(DIRECTIONS), 0., 1.);
  216. #if SCENE_CLIP_BOX == 1
  217. ao = mix(ao, 1., smoothstep(0., radiusToUse, boxDistance));
  218. #endif
  219. ao = pow(ao, scale);
  220. gl_FragColor = FRAGMENT_OUTPUT;
  221. }`
  222. };
  223. /**
  224. * GTAO depth shader. Use by {@link GTAOPass}.
  225. *
  226. * @constant
  227. * @type {Object}
  228. */
  229. const GTAODepthShader = {
  230. name: 'GTAODepthShader',
  231. defines: {
  232. PERSPECTIVE_CAMERA: 1
  233. },
  234. uniforms: {
  235. tDepth: { value: null },
  236. cameraNear: { value: null },
  237. cameraFar: { value: null },
  238. },
  239. vertexShader: /* glsl */`
  240. varying vec2 vUv;
  241. void main() {
  242. vUv = uv;
  243. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  244. }`,
  245. fragmentShader: /* glsl */`
  246. uniform sampler2D tDepth;
  247. uniform float cameraNear;
  248. uniform float cameraFar;
  249. varying vec2 vUv;
  250. #include <packing>
  251. float getLinearDepth( const in vec2 screenPosition ) {
  252. #if PERSPECTIVE_CAMERA == 1
  253. float fragCoordZ = texture2D( tDepth, screenPosition ).x;
  254. float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
  255. return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
  256. #else
  257. return texture2D( tDepth, screenPosition ).x;
  258. #endif
  259. }
  260. void main() {
  261. float depth = getLinearDepth( vUv );
  262. gl_FragColor = vec4( vec3( 1.0 - depth ), 1.0 );
  263. }`
  264. };
  265. /**
  266. * GTAO blend shader. Use by {@link GTAOPass}.
  267. *
  268. * @constant
  269. * @type {Object}
  270. */
  271. const GTAOBlendShader = {
  272. name: 'GTAOBlendShader',
  273. uniforms: {
  274. tDiffuse: { value: null },
  275. intensity: { value: 1.0 }
  276. },
  277. vertexShader: /* glsl */`
  278. varying vec2 vUv;
  279. void main() {
  280. vUv = uv;
  281. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  282. }`,
  283. fragmentShader: /* glsl */`
  284. uniform float intensity;
  285. uniform sampler2D tDiffuse;
  286. varying vec2 vUv;
  287. void main() {
  288. vec4 texel = texture2D( tDiffuse, vUv );
  289. gl_FragColor = vec4(mix(vec3(1.), texel.rgb, intensity), texel.a);
  290. }`
  291. };
  292. function generateMagicSquareNoise( size = 5 ) {
  293. const noiseSize = Math.floor( size ) % 2 === 0 ? Math.floor( size ) + 1 : Math.floor( size );
  294. const magicSquare = generateMagicSquare( noiseSize );
  295. const noiseSquareSize = magicSquare.length;
  296. const data = new Uint8Array( noiseSquareSize * 4 );
  297. for ( let inx = 0; inx < noiseSquareSize; ++ inx ) {
  298. const iAng = magicSquare[ inx ];
  299. const angle = ( 2 * Math.PI * iAng ) / noiseSquareSize;
  300. const randomVec = new Vector3(
  301. Math.cos( angle ),
  302. Math.sin( angle ),
  303. 0
  304. ).normalize();
  305. data[ inx * 4 ] = ( randomVec.x * 0.5 + 0.5 ) * 255;
  306. data[ inx * 4 + 1 ] = ( randomVec.y * 0.5 + 0.5 ) * 255;
  307. data[ inx * 4 + 2 ] = 127;
  308. data[ inx * 4 + 3 ] = 255;
  309. }
  310. const noiseTexture = new DataTexture( data, noiseSize, noiseSize );
  311. noiseTexture.wrapS = RepeatWrapping;
  312. noiseTexture.wrapT = RepeatWrapping;
  313. noiseTexture.needsUpdate = true;
  314. return noiseTexture;
  315. }
  316. function generateMagicSquare( size ) {
  317. const noiseSize = Math.floor( size ) % 2 === 0 ? Math.floor( size ) + 1 : Math.floor( size );
  318. const noiseSquareSize = noiseSize * noiseSize;
  319. const magicSquare = Array( noiseSquareSize ).fill( 0 );
  320. let i = Math.floor( noiseSize / 2 );
  321. let j = noiseSize - 1;
  322. for ( let num = 1; num <= noiseSquareSize; ) {
  323. if ( i === - 1 && j === noiseSize ) {
  324. j = noiseSize - 2;
  325. i = 0;
  326. } else {
  327. if ( j === noiseSize ) {
  328. j = 0;
  329. }
  330. if ( i < 0 ) {
  331. i = noiseSize - 1;
  332. }
  333. }
  334. if ( magicSquare[ i * noiseSize + j ] !== 0 ) {
  335. j -= 2;
  336. i ++;
  337. continue;
  338. } else {
  339. magicSquare[ i * noiseSize + j ] = num ++;
  340. }
  341. j ++;
  342. i --;
  343. }
  344. return magicSquare;
  345. }
  346. export { generateMagicSquareNoise, GTAOShader, GTAODepthShader, GTAOBlendShader };
粤ICP备19079148号