webgpu_postprocessing_ao.html 19 KB

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