webgpu_postprocessing_ao.html 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgpu - ambient occlusion</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  7. <meta property="og:title" content="three.js webgpu - ambient occlusion">
  8. <meta property="og:type" content="website">
  9. <meta property="og:url" content="https://threejs.org/examples/webgpu_postprocessing_ao.html">
  10. <meta property="og:image" content="https://threejs.org/examples/screenshots/webgpu_postprocessing_ao.jpg">
  11. <link type="text/css" rel="stylesheet" href="example.css">
  12. </head>
  13. <body>
  14. <div id="info">
  15. <a href="https://threejs.org/" target="_blank" rel="noopener" class="logo-link"></a>
  16. <div class="title-wrapper">
  17. <a href="https://threejs.org/" target="_blank" rel="noopener">three.js</a><span>AO</span>
  18. </div>
  19. <small>
  20. Ambient Occlusion based on GTAO.<br />
  21. Tennyson bust from <a href="https://threedscans.com/lincoln/tennyson/" target="_blank" rel="noopener">Three D Scans</a>.
  22. </small>
  23. </div>
  24. <script type="importmap">
  25. {
  26. "imports": {
  27. "three": "../build/three.webgpu.js",
  28. "three/webgpu": "../build/three.webgpu.js",
  29. "three/tsl": "../build/three.tsl.js",
  30. "three/addons/": "./jsm/"
  31. }
  32. }
  33. </script>
  34. <script type="module">
  35. import * as THREE from 'three/webgpu';
  36. import { sample, pass, mrt, screenUV, normalView, velocity, vec3, vec4, packNormalToRGB, unpackRGBToNormal, colorSpaceToWorking, builtinAOContext, texture } from 'three/tsl';
  37. import { ao } from 'three/addons/tsl/display/GTAONode.js';
  38. import { traa } from 'three/addons/tsl/display/TRAANode.js';
  39. import { generateBlueNoiseTexture } from 'three/addons/math/BlueNoise.js';
  40. import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  41. import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
  42. import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
  43. import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
  44. import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';
  45. import { Inspector } from 'three/addons/inspector/Inspector.js';
  46. let camera, scene, renderer, renderPipeline, controls;
  47. let aoPass, traaPass, transparentMesh;
  48. const params = {
  49. samples: 14,
  50. radius: 0.5,
  51. scale: 0.8,
  52. thickness: 1,
  53. blueNoise: false,
  54. aoOnly: false,
  55. transparentOpacity: 0.3
  56. };
  57. let blueNoiseTexture = null; // generated lazily on first enable
  58. init();
  59. async function init() {
  60. camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 50 );
  61. camera.position.set( 1, 3, 7 );
  62. scene = new THREE.Scene();
  63. renderer = new THREE.WebGPURenderer();
  64. renderer.toneMapping = THREE.NeutralToneMapping;
  65. renderer.setPixelRatio( window.devicePixelRatio );
  66. renderer.setSize( window.innerWidth, window.innerHeight );
  67. renderer.setAnimationLoop( animate );
  68. renderer.inspector = new Inspector();
  69. document.body.appendChild( renderer.domElement );
  70. await renderer.init();
  71. // controls
  72. controls = new OrbitControls( camera, renderer.domElement );
  73. controls.enableDamping = true;
  74. controls.minDistance = 2;
  75. controls.maxDistance = 16;
  76. controls.target.set( 0, 1.2, 0 );
  77. // environment
  78. const environment = new RoomEnvironment();
  79. const pmremGenerator = new THREE.PMREMGenerator( renderer );
  80. scene.background = new THREE.Color( 0x666666 );
  81. scene.environment = pmremGenerator.fromScene( environment, 0.04 ).texture;
  82. scene.environmentIntensity = 0.3;
  83. environment.dispose();
  84. pmremGenerator.dispose();
  85. // post-processing
  86. renderPipeline = new THREE.RenderPipeline( renderer );
  87. // pre-pass
  88. const prePass = pass( scene, camera ).toInspector( 'Normal', ( inspectNode ) => colorSpaceToWorking( inspectNode, THREE.SRGBColorSpace ) );
  89. prePass.name = 'Pre-Pass';
  90. prePass.transparent = false;
  91. prePass.setMRT( mrt( {
  92. output: packNormalToRGB( normalView ),
  93. velocity: velocity
  94. } ) );
  95. const prePassNormal = sample( ( uv ) => {
  96. return unpackRGBToNormal( prePass.getTextureNode().sample( uv ) );
  97. } );
  98. const prePassDepth = prePass.getTextureNode( 'depth' ).toInspector( 'Depth', () => prePass.getLinearDepthNode() );
  99. const prePassVelocity = prePass.getTextureNode( 'velocity' ).toInspector( 'Velocity' );
  100. // pre-pass - bandwidth optimization
  101. const normalTexture = prePass.getTexture( 'output' );
  102. normalTexture.type = THREE.UnsignedByteType;
  103. // scene pass
  104. const scenePass = pass( scene, camera ).toInspector( 'Color' );
  105. // ao
  106. aoPass = ao( prePassDepth, prePassNormal, camera, prePassVelocity ).toInspector( 'GTAO', ( inspectNode ) => inspectNode.r );
  107. aoPass.resolutionScale = 0.5; // running AO in half resolution is often sufficient
  108. aoPass.temporalAccumulation = false; // single-pass, no temporal resolve
  109. aoPass.jitter = false; // static noise pattern: avoids per-frame crawl (leaves a faint fixed dither)
  110. const aoPassOutput = aoPass.getTextureNode();
  111. // scene context
  112. scenePass.contextNode = builtinAOContext( aoPassOutput.sample( screenUV ).r );
  113. // final output + traa
  114. traaPass = traa( scenePass, prePassDepth, prePassVelocity, camera );
  115. traaPass.useSubpixelCorrection = false;
  116. renderPipeline.outputNode = traaPass;
  117. // models
  118. const dracoLoader = new DRACOLoader();
  119. const loader = new GLTFLoader();
  120. loader.setDRACOLoader( dracoLoader );
  121. loader.setPath( 'models/gltf/' );
  122. // wall-mounted spotlights
  123. function addSpotLight( position, targetPosition ) {
  124. const light = new THREE.SpotLight( 0xffe09e, 40 );
  125. light.position.copy( position );
  126. light.angle = 0.6;
  127. light.penumbra = 1;
  128. light.distance = 6.5;
  129. light.decay = 2;
  130. light.target.position.copy( targetPosition );
  131. scene.add( light );
  132. scene.add( light.target );
  133. }
  134. addSpotLight( new THREE.Vector3( - 2.5, 5, - 4.8 ), new THREE.Vector3( - 2.5, - 2, - 4.8 ) );
  135. addSpotLight( new THREE.Vector3( 2.5, 5, - 4.8 ), new THREE.Vector3( 2.5, - 2, - 4.8 ) );
  136. addSpotLight( new THREE.Vector3( - 5.3, 5, 0 ), new THREE.Vector3( - 5.3, - 2, 0 ) );
  137. // checkerboard floor
  138. const tilesX = 16;
  139. const tilesZ = 16;
  140. const floorCanvas = document.createElement( 'canvas' );
  141. floorCanvas.width = tilesX * 32;
  142. floorCanvas.height = tilesZ * 32;
  143. const floorCtx = floorCanvas.getContext( '2d' );
  144. for ( let x = 0; x < tilesX; x ++ ) {
  145. for ( let z = 0; z < tilesZ; z ++ ) {
  146. floorCtx.fillStyle = ( x + z ) % 2 === 0 ? '#d8d0c8' : '#b8b0a8';
  147. floorCtx.fillRect( x * 32, z * 32, 32, 32 );
  148. }
  149. }
  150. const floorTexture = new THREE.CanvasTexture( floorCanvas );
  151. floorTexture.colorSpace = THREE.SRGBColorSpace;
  152. floorTexture.wrapS = THREE.RepeatWrapping;
  153. floorTexture.wrapT = THREE.RepeatWrapping;
  154. const floorMat = new THREE.MeshStandardMaterial( { map: floorTexture, roughness: 0.7, metalness: 0.05 } );
  155. const floor = new THREE.Mesh( new THREE.PlaneGeometry( tilesX, tilesZ ), floorMat );
  156. floor.rotation.x = - Math.PI / 2;
  157. floor.position.y = - 2;
  158. scene.add( floor );
  159. // walls
  160. const wallMat = new THREE.MeshStandardMaterial( { color: '#e0d8d0', roughness: 0.9, metalness: 0 } );
  161. const backWall = new THREE.Mesh( new THREE.PlaneGeometry( 16, 10 ), wallMat );
  162. backWall.position.set( 0, 3, - 5 );
  163. scene.add( backWall );
  164. const leftWall = new THREE.Mesh( new THREE.PlaneGeometry( 16, 10 ), wallMat );
  165. leftWall.rotation.y = Math.PI / 2;
  166. leftWall.position.set( - 5.5, 3, 0 );
  167. scene.add( leftWall );
  168. // central pedestal
  169. const pedestalMat = new THREE.MeshStandardMaterial( { color: '#f0ece8', roughness: 0.4, metalness: 0.05 } );
  170. const pedestalBase = new THREE.Mesh( new THREE.CylinderGeometry( 0.9, 1.0, 0.2, 32 ), pedestalMat );
  171. pedestalBase.position.set( 0, - 1.9, 0 );
  172. scene.add( pedestalBase );
  173. const pedestalShaft = new THREE.Mesh( new THREE.CylinderGeometry( 0.55, 0.65, 1.5, 32 ), pedestalMat );
  174. pedestalShaft.position.set( 0, - 1.05, 0 );
  175. scene.add( pedestalShaft );
  176. const pedestalTop = new THREE.Mesh( new THREE.CylinderGeometry( 0.8, 0.7, 0.25, 32 ), pedestalMat );
  177. pedestalTop.position.set( 0, - 0.18, 0 );
  178. scene.add( pedestalTop );
  179. // torus knot
  180. const knotMat = new THREE.MeshStandardMaterial( { color: '#c0a060', roughness: 0.45, metalness: 0 } );
  181. const torusKnot = new THREE.Mesh( new THREE.TorusKnotGeometry( 0.5, 0.17, 128, 32 ), knotMat );
  182. torusKnot.position.set( 0, 0.82, 0 );
  183. scene.add( torusKnot );
  184. // columns
  185. const columnMat = new THREE.MeshStandardMaterial( { color: '#e8e4de', roughness: 0.5, metalness: 0 } );
  186. function addColumn( x, z ) {
  187. const columnGroup = new THREE.Group();
  188. const base = new THREE.Mesh( new THREE.BoxGeometry( 0.6, 0.2, 0.6 ), columnMat );
  189. base.position.y = - 1.9;
  190. columnGroup.add( base );
  191. const shaft = new THREE.Mesh( new THREE.CylinderGeometry( 0.18, 0.22, 5, 16 ), columnMat );
  192. shaft.position.y = 0.7;
  193. columnGroup.add( shaft );
  194. const capital = new THREE.Mesh( new THREE.BoxGeometry( 0.55, 0.25, 0.55 ), columnMat );
  195. capital.position.y = 3.35;
  196. columnGroup.add( capital );
  197. columnGroup.scale.setScalar( 1.5 );
  198. columnGroup.position.set( x, 1.0, z );
  199. scene.add( columnGroup );
  200. }
  201. addColumn( - 5.05, - 4.55 );
  202. addColumn( 4.5, - 4.55 );
  203. addColumn( - 5.05, 3 );
  204. // vase on its own pedestal
  205. const vaseProfile = [
  206. new THREE.Vector2( 0, 0.5 ),
  207. new THREE.Vector2( 0.12, 0.5 ),
  208. new THREE.Vector2( 0.18, 0.7 ),
  209. new THREE.Vector2( 0.28, 0.9 ),
  210. new THREE.Vector2( 0.32, 1.0 ),
  211. new THREE.Vector2( 0.3, 1.1 ),
  212. new THREE.Vector2( 0.22, 1.15 ),
  213. new THREE.Vector2( 0.2, 1.2 ),
  214. new THREE.Vector2( 0.22, 1.25 ),
  215. new THREE.Vector2( 0, 1.25 )
  216. ];
  217. const vaseMat = new THREE.MeshStandardMaterial( { color: '#d4806a', roughness: 0.6, metalness: 0.02 } );
  218. const vasePedestalBase = new THREE.Mesh( new THREE.CylinderGeometry( 0.6, 0.7, 0.15, 32 ), pedestalMat );
  219. vasePedestalBase.position.set( - 5, - 1.925, - 2 );
  220. scene.add( vasePedestalBase );
  221. const vasePedestalShaft = new THREE.Mesh( new THREE.CylinderGeometry( 0.35, 0.42, 1, 32 ), pedestalMat );
  222. vasePedestalShaft.position.set( - 5, - 1.35, - 2 );
  223. scene.add( vasePedestalShaft );
  224. const vasePedestalTop = new THREE.Mesh( new THREE.CylinderGeometry( 0.55, 0.48, 0.15, 32 ), pedestalMat );
  225. vasePedestalTop.position.set( - 5, - 0.775, - 2 );
  226. scene.add( vasePedestalTop );
  227. const vase = new THREE.Mesh( new THREE.LatheGeometry( vaseProfile, 24 ), vaseMat );
  228. vase.position.set( - 5, - 1.6, - 2 );
  229. vase.scale.setScalar( 1.8 );
  230. scene.add( vase );
  231. // armchair
  232. const woodMat = new THREE.MeshStandardMaterial( { color: '#8b6840', roughness: 0.8, metalness: 0 } );
  233. const fabricMat = new THREE.MeshStandardMaterial( { color: '#8b3a3a', roughness: 0.9, metalness: 0 } );
  234. const chairGroup = new THREE.Group();
  235. const seat = new THREE.Mesh( new RoundedBoxGeometry( 0.9, 0.25, 0.8, 4, 0.06 ), fabricMat );
  236. seat.position.set( 0, - 1.35, 0 );
  237. chairGroup.add( seat );
  238. const backrest = new THREE.Mesh( new RoundedBoxGeometry( 0.9, 0.6, 0.12, 4, 0.04 ), fabricMat );
  239. backrest.position.set( 0, - 0.95, - 0.4 );
  240. backrest.rotation.x = - 0.3;
  241. chairGroup.add( backrest );
  242. for ( const side of [ - 1, 1 ] ) {
  243. const armrest = new THREE.Mesh( new THREE.BoxGeometry( 0.1, 0.25, 0.7 ), woodMat );
  244. armrest.position.set( side * 0.5, - 1.2, 0 );
  245. chairGroup.add( armrest );
  246. const armTop = new THREE.Mesh( new THREE.BoxGeometry( 0.14, 0.06, 0.8 ), woodMat );
  247. armTop.position.set( side * 0.5, - 1.07, 0 );
  248. chairGroup.add( armTop );
  249. }
  250. const legPositions = [[ - 0.38, - 0.32 ], [ - 0.38, 0.32 ], [ 0.38, - 0.32 ], [ 0.38, 0.32 ]];
  251. for ( const [ lx, lz ] of legPositions ) {
  252. const leg = new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0.035, 0.2, 8 ), woodMat );
  253. leg.position.set( lx, - 1.575, lz );
  254. chairGroup.add( leg );
  255. }
  256. chairGroup.scale.setScalar( 1.76 );
  257. chairGroup.position.set( 4, 0.948, - 2.5 );
  258. chairGroup.rotation.y = - 0.6;
  259. scene.add( chairGroup );
  260. // side table with cup
  261. const tableGroup = new THREE.Group();
  262. const tableTop = new THREE.Mesh( new THREE.CylinderGeometry( 0.4, 0.4, 0.05, 24 ), woodMat );
  263. tableTop.position.y = - 1.05;
  264. tableGroup.add( tableTop );
  265. const tableLeg = new THREE.Mesh( new THREE.CylinderGeometry( 0.04, 0.06, 0.9, 8 ), woodMat );
  266. tableLeg.position.y = - 1.5;
  267. tableGroup.add( tableLeg );
  268. const tableBase = new THREE.Mesh( new THREE.CylinderGeometry( 0.25, 0.28, 0.06, 24 ), woodMat );
  269. tableBase.position.y = - 1.92;
  270. tableGroup.add( tableBase );
  271. const cupMat = new THREE.MeshStandardMaterial( { color: '#f0ece0', roughness: 0.4, metalness: 0.05 } );
  272. const cupBody = new THREE.Mesh( new THREE.CylinderGeometry( 0.1, 0.08, 0.2, 16 ), cupMat );
  273. cupBody.scale.setScalar( 1 / 2.2 );
  274. cupBody.position.set( 0.15, - 0.98, 0 );
  275. tableGroup.add( cupBody );
  276. tableGroup.scale.setScalar( 2.2 );
  277. tableGroup.position.set( 2, 2.29, - 4 );
  278. scene.add( tableGroup );
  279. // rug
  280. const rugMat = new THREE.MeshStandardMaterial( { color: '#c8a0a8', roughness: 0.95, metalness: 0 } );
  281. const rug = new THREE.Mesh( new THREE.BoxGeometry( 6, 0.02, 5 ), rugMat );
  282. rug.position.set( 0, - 1.99, 0.5 );
  283. scene.add( rug );
  284. const rugBorderMat = new THREE.MeshStandardMaterial( { color: '#d4b880', roughness: 0.95, metalness: 0 } );
  285. const rugBorder = new THREE.Mesh( new THREE.BoxGeometry( 6.3, 0.015, 5.3 ), rugBorderMat );
  286. rugBorder.position.set( 0, - 1.9925, 0.5 );
  287. scene.add( rugBorder );
  288. // picture frames
  289. function addFrame( x, y, z, width, height, paintColor, rotY = 0 ) {
  290. const frameGroup = new THREE.Group();
  291. const t = 0.1;
  292. const outerW = width + t * 2;
  293. const outerH = height + t * 2;
  294. const frameShape = new THREE.Shape();
  295. frameShape.moveTo( - outerW / 2, - outerH / 2 );
  296. frameShape.lineTo( outerW / 2, - outerH / 2 );
  297. frameShape.lineTo( outerW / 2, outerH / 2 );
  298. frameShape.lineTo( - outerW / 2, outerH / 2 );
  299. frameShape.closePath();
  300. const hole = new THREE.Path();
  301. hole.moveTo( - width / 2, - height / 2 );
  302. hole.lineTo( width / 2, - height / 2 );
  303. hole.lineTo( width / 2, height / 2 );
  304. hole.lineTo( - width / 2, height / 2 );
  305. hole.closePath();
  306. frameShape.holes.push( hole );
  307. const frameMat = new THREE.MeshStandardMaterial( { color: '#8b6840', roughness: 0.7, metalness: 0 } );
  308. const geo = new THREE.ExtrudeGeometry( frameShape, {
  309. depth: 0.12,
  310. bevelEnabled: true,
  311. bevelThickness: 0.02,
  312. bevelSize: 0.02,
  313. bevelSegments: 2
  314. } );
  315. const frame = new THREE.Mesh( geo, frameMat );
  316. frameGroup.add( frame );
  317. const paintMat = new THREE.MeshStandardMaterial( { color: paintColor, roughness: 0.95, metalness: 0 } );
  318. const canvas = new THREE.Mesh( new THREE.PlaneGeometry( width, height ), paintMat );
  319. canvas.position.z = 0.001;
  320. frameGroup.add( canvas );
  321. frameGroup.position.set( x, y, z );
  322. if ( rotY ) frameGroup.rotation.y = rotY;
  323. scene.add( frameGroup );
  324. }
  325. addFrame( - 3.2, 2.0, - 4.9, 1.8, 1.2, '#e8a8a0' );
  326. addFrame( - 0.5, 2.6, - 4.9, 1.1, 1.6, '#a0c0e0' );
  327. addFrame( 2, 1.8, - 4.9, 2.0, 1.4, '#a0d0a8' );
  328. addFrame( - 5.4, 2.2, - 3, 1.5, 1.1, '#d0b0d8', Math.PI / 2 );
  329. addFrame( - 5.4, 1.8, 1, 1.8, 1.3, '#e0c8a0', Math.PI / 2 );
  330. // bust pedestal
  331. const bustX = - 3;
  332. const bustZ = - 3.2;
  333. const bustBase = new THREE.Mesh( new THREE.CylinderGeometry( 0.6, 0.7, 0.15, 32 ), pedestalMat );
  334. bustBase.position.set( bustX, - 1.925, bustZ );
  335. scene.add( bustBase );
  336. const bustShaft = new THREE.Mesh( new THREE.CylinderGeometry( 0.35, 0.42, 1, 32 ), pedestalMat );
  337. bustShaft.position.set( bustX, - 1.35, bustZ );
  338. scene.add( bustShaft );
  339. const bustTop = new THREE.Mesh( new THREE.CylinderGeometry( 0.55, 0.48, 0.15, 32 ), pedestalMat );
  340. bustTop.position.set( bustX, - 0.775, bustZ );
  341. scene.add( bustTop );
  342. // Transparent plane for testing
  343. transparentMesh = new THREE.Mesh( new THREE.PlaneGeometry( 1.8, 2 ), new THREE.MeshStandardNodeMaterial( { transparent: true, opacity: params.transparentOpacity } ) );
  344. transparentMesh.visible = false;
  345. transparentMesh.position.set( 0, 1.2, 1.5 );
  346. scene.add( transparentMesh );
  347. updateParameters();
  348. // bust GLB
  349. const gltf = await loader.loadAsync( 'tennyson-bust.glb' );
  350. const bust = gltf.scene;
  351. bust.rotation.y = Math.PI;
  352. const targetHeight = 2.64;
  353. const sizeBox = new THREE.Box3().setFromObject( bust );
  354. const size = new THREE.Vector3();
  355. sizeBox.getSize( size );
  356. bust.scale.setScalar( targetHeight / size.y );
  357. const fitBox = new THREE.Box3().setFromObject( bust );
  358. const center = new THREE.Vector3();
  359. fitBox.getCenter( center );
  360. bust.position.set( bustX - center.x, - 0.7 - fitBox.min.y, bustZ - center.z + 0.1 );
  361. scene.add( bust );
  362. // events
  363. window.addEventListener( 'resize', onWindowResize );
  364. // GUI
  365. const gui = renderer.inspector.createParameters( 'Settings' );
  366. gui.add( params, 'samples', 4, 32, 1 ).onChange( updateParameters );
  367. gui.add( params, 'radius', 0.1, 1 ).onChange( updateParameters );
  368. gui.add( params, 'scale', 0.01, 1 ).onChange( updateParameters );
  369. gui.add( params, 'thickness', 0.01, 2 ).onChange( updateParameters );
  370. gui.add( params, 'blueNoise' ).name( 'blue noise' ).onChange( ( value ) => {
  371. // IGN by default; blue noise generated lazily on first enable
  372. if ( value && blueNoiseTexture === null ) blueNoiseTexture = texture( generateBlueNoiseTexture( 64, 2 ) );
  373. aoPass.noiseNode = value ? blueNoiseTexture : null;
  374. } );
  375. gui.add( aoPass, 'jitter' ).name( 'jitter' );
  376. gui.add( aoPass, 'temporalAccumulation' ).name( 'temporal accumulation' );
  377. gui.add( aoPass.temporalAccumulationAlpha, 'value', 0.02, 1, 0.01 ).name( 'accumulation alpha' );
  378. gui.add( transparentMesh, 'visible' ).name( 'show transparent mesh' );
  379. gui.add( params, 'transparentOpacity', 0, 1, 0.01 ).name( 'transparent opacity' ).onChange( updateParameters );
  380. gui.add( params, 'aoOnly' ).onChange( ( value ) => {
  381. if ( value === true ) {
  382. renderPipeline.outputNode = vec4( vec3( aoPass.r ), 1 );
  383. renderer.toneMapping = THREE.NoToneMapping;
  384. } else {
  385. renderPipeline.outputNode = traaPass;
  386. renderer.toneMapping = THREE.NeutralToneMapping;
  387. }
  388. renderPipeline.needsUpdate = true;
  389. } );
  390. }
  391. function updateParameters() {
  392. aoPass.samples.value = params.samples;
  393. aoPass.radius.value = params.radius;
  394. aoPass.scale.value = params.scale;
  395. aoPass.thickness.value = params.thickness;
  396. transparentMesh.material.opacity = params.transparentOpacity;
  397. }
  398. function onWindowResize() {
  399. const width = window.innerWidth;
  400. const height = window.innerHeight;
  401. camera.aspect = width / height;
  402. camera.updateProjectionMatrix();
  403. renderer.setSize( width, height );
  404. }
  405. function animate() {
  406. controls.update();
  407. renderPipeline.render();
  408. }
  409. </script>
  410. </body>
  411. </html>
粤ICP备19079148号