webgpu_postprocessing_ao.html 21 KB

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