webgpu_postprocessing_ao.html 18 KB

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