webgpu_postprocessing_ao.html 19 KB

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