webgpu_materialx_noise.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgpu - materialx noise</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 - materialx noise">
  8. <meta property="og:type" content="website">
  9. <meta property="og:url" content="https://threejs.org/examples/webgpu_materialx_noise.html">
  10. <meta property="og:image" content="https://threejs.org/examples/screenshots/webgpu_materialx_noise.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>MaterialX Noise</span>
  18. </div>
  19. <small>MaterialX noise functions.</small>
  20. </div>
  21. <script type="importmap">
  22. {
  23. "imports": {
  24. "three": "../build/three.webgpu.js",
  25. "three/webgpu": "../build/three.webgpu.js",
  26. "three/tsl": "../build/three.tsl.js",
  27. "three/addons/": "./jsm/"
  28. }
  29. }
  30. </script>
  31. <script type="module">
  32. import * as THREE from 'three/webgpu';
  33. import {
  34. Fn, add, dot, element, float, floor, int, mix, mul, normalWorld, sub, time, uv, vec2, vec3,
  35. mx_cell_noise_float,
  36. mx_fractal_noise_vec3,
  37. mx_modulo,
  38. mx_noise_vec3,
  39. mx_unifiednoise2d,
  40. mx_unifiednoise3d,
  41. mx_worley_noise_float_2d,
  42. mx_worley_noise_float_3d
  43. } from 'three/tsl';
  44. import { Inspector } from 'three/addons/inspector/Inspector.js';
  45. import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  46. import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js';
  47. import { FontLoader } from 'three/addons/loaders/FontLoader.js';
  48. import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
  49. import { mxHextileComputeBlendWeights, mxHextileCoord } from 'three/addons/loaders/materialx/MaterialXHextile.js';
  50. let container;
  51. let camera, scene, renderer;
  52. let particleLight;
  53. let group;
  54. const spheres = [];
  55. init();
  56. async function init() {
  57. container = document.createElement( 'div' );
  58. document.body.appendChild( container );
  59. camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 1000 );
  60. camera.position.z = 190;
  61. scene = new THREE.Scene();
  62. group = new THREE.Group();
  63. scene.add( group );
  64. const [ hdrTexture, font ] = await Promise.all( [
  65. new HDRCubeTextureLoader()
  66. .setPath( 'textures/cube/pisaHDR/' )
  67. .loadAsync( [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ] ),
  68. new FontLoader().loadAsync( 'fonts/helvetiker_regular.typeface.json' )
  69. ] );
  70. const geometry = new THREE.SphereGeometry( 5, 64, 32 );
  71. const labelMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff } );
  72. const position2D = uv( 0 ).mul( 12 ).add( vec2( time.mul( .35 ), time.mul( .19 ) ) );
  73. const position3D = normalWorld.mul( 4 ).add( vec3( time.mul( .35 ), time.mul( .19 ), time.mul( .27 ) ) );
  74. const fractal2D = vec3( position2D.x, position2D.y, 0 ).mul( .35 );
  75. const fractal3D = position3D.mul( .35 );
  76. const unifiedOffset2D = vec2( time.mul( .35 ), time.mul( .19 ) );
  77. const unifiedOffset3D = vec3( time.mul( .35 ), time.mul( .19 ), time.mul( .27 ) );
  78. const unifiedNoise3D = ( noiseType, position, freq, offset, jitter = 1, outmin = 0, outmax = 1, clampoutput = false, octaves = 1, lacunarity = 2, diminish = .5, style = 0 ) => Fn( () => mx_unifiednoise3d( noiseType, position, freq, offset, jitter, outmin, outmax, clampoutput, octaves, lacunarity, diminish, style ) )();
  79. const textileTexcoord = ( speedX, speedY ) => uv( 0 ).mul( 3 ).add( vec2( time.mul( speedX ), time.mul( speedY ) ) );
  80. const checkerboard = ( color1, color2, uvtiling, uvoffset, texcoord ) => {
  81. const tiledUv = sub( mul( texcoord, uvtiling ), uvoffset );
  82. const checkerMix = mx_modulo( dot( floor( tiledUv ), vec2( 1, 1 ) ), float( 2 ) );
  83. return mix( color2, color1, checkerMix );
  84. };
  85. const hextiledCheckerboard = ( texcoord, {
  86. color1 = vec3( 1, 1, 1 ),
  87. color2 = vec3( 0, 0, 0 ),
  88. uvtiling = vec2( 4, 4 ),
  89. uvoffset = vec2( 0, 0 ),
  90. tiling = vec2( 1, 1 ),
  91. rotation = 0,
  92. rotationrange = vec2( 0, 360 ),
  93. scale = 0,
  94. scalerange = vec2( .5, 2 ),
  95. offset = 0,
  96. offsetrange = vec2( 0, 1 ),
  97. falloff = .5,
  98. falloffcontrast = .5,
  99. lumacoeffs = vec3( 0.2722287, 0.6740818, 0.0536895 )
  100. } = {} ) => {
  101. const tileData = mxHextileCoord( mul( texcoord, tiling ), rotation, rotationrange, scale, scalerange, offset, offsetrange );
  102. const c0 = checkerboard( color1, color2, uvtiling, uvoffset, tileData.coords[ 0 ] );
  103. const c1 = checkerboard( color1, color2, uvtiling, uvoffset, tileData.coords[ 1 ] );
  104. const c2 = checkerboard( color1, color2, uvtiling, uvoffset, tileData.coords[ 2 ] );
  105. const luminanceWeights = mix(
  106. vec3( 1, 1, 1 ),
  107. vec3( dot( c0, lumacoeffs ), dot( c1, lumacoeffs ), dot( c2, lumacoeffs ) ),
  108. vec3( falloffcontrast, falloffcontrast, falloffcontrast )
  109. );
  110. const blendWeights = mxHextileComputeBlendWeights( luminanceWeights, tileData.weights, falloff );
  111. return add( add( mul( element( blendWeights, 0 ), c0 ), mul( element( blendWeights, 1 ), c1 ) ), mul( element( blendWeights, 2 ), c2 ) );
  112. };
  113. const noiseExamples = [
  114. { label: [ 'Noise', '2D' ], createNode: () => mx_noise_vec3( position2D ) },
  115. { label: [ 'Noise', '3D' ], createNode: () => mx_noise_vec3( position3D ) },
  116. { label: [ 'Cellnoise', '2D' ], createNode: () => mx_cell_noise_float( position2D ) },
  117. { label: [ 'Cellnoise', '3D' ], createNode: () => mx_cell_noise_float( position3D ) },
  118. { label: [ 'Worley', '2D style 0' ], createNode: () => mx_worley_noise_float_2d( position2D, 1, 0 ) },
  119. { label: [ 'Worley', '3D style 0' ], createNode: () => mx_worley_noise_float_3d( position3D, 1, 0 ) },
  120. { label: [ 'Worley', '2D style 1' ], createNode: () => mx_worley_noise_float_2d( position2D, 1, 1 ) },
  121. { label: [ 'Worley', '3D style 1' ], createNode: () => mx_worley_noise_float_3d( position3D, 1, 1 ) },
  122. { label: [ 'Fractal', '2D' ], createNode: () => mx_fractal_noise_vec3( fractal2D ) },
  123. { label: [ 'Fractal', '3D' ], createNode: () => mx_fractal_noise_vec3( fractal3D ) },
  124. { label: [ 'Unified Perlin', '2D' ], createNode: () => mx_unifiednoise2d( int( 0 ), position2D, vec2( 1, 1 ), unifiedOffset2D ) },
  125. { label: [ 'Unified Perlin', '3D' ], createNode: () => unifiedNoise3D( int( 0 ), position3D, vec3( 1, 1, 1 ), unifiedOffset3D ) },
  126. { label: [ 'Unified Cell', '2D' ], createNode: () => mx_unifiednoise2d( int( 1 ), position2D, vec2( 1, 1 ), unifiedOffset2D ) },
  127. { label: [ 'Unified Cell', '3D' ], createNode: () => unifiedNoise3D( int( 1 ), position3D, vec3( 1, 1, 1 ), unifiedOffset3D ) },
  128. { label: [ 'Unified Worley', '2D style 0' ], createNode: () => mx_unifiednoise2d( int( 2 ), position2D, vec2( 1, 1 ), unifiedOffset2D, 1, 0, 1, false, 1, 2, .5, 0 ) },
  129. { label: [ 'Unified Worley', '3D style 0' ], createNode: () => unifiedNoise3D( int( 2 ), position3D, vec3( 1, 1, 1 ), unifiedOffset3D, 1, 0, 1, false, 1, 2, .5, 0 ) },
  130. { label: [ 'Unified Worley', '2D style 1' ], createNode: () => mx_unifiednoise2d( int( 2 ), position2D, vec2( 1, 1 ), unifiedOffset2D, 1, 0, 1, false, 1, 2, .5, 1 ) },
  131. { label: [ 'Unified Worley', '3D style 1' ], createNode: () => unifiedNoise3D( int( 2 ), position3D, vec3( 1, 1, 1 ), unifiedOffset3D, 1, 0, 1, false, 1, 2, .5, 1 ) },
  132. { label: [ 'Unified Fractal', '2D' ], createNode: () => mx_unifiednoise2d( int( 3 ), position2D, vec2( 1, 1 ), unifiedOffset2D, 1, 0, 1, false, 3 ) },
  133. { label: [ 'Unified Fractal', '3D' ], createNode: () => unifiedNoise3D( int( 3 ), position3D, vec3( 1, 1, 1 ), unifiedOffset3D, 1, 0, 1, false, 3 ) },
  134. {
  135. label: [ 'Hex Tiled', 'checkerboard' ],
  136. createNode: () => hextiledCheckerboard( textileTexcoord( .08, .04 ), {
  137. color1: vec3( 1, 1, 1 ),
  138. color2: vec3( 0, 0, 0 ),
  139. uvtiling: vec2( 8, 8 ),
  140. tiling: vec2( 2.2, 2.2 ),
  141. rotation: .25,
  142. scale: .1,
  143. offset: .2,
  144. falloff: .35,
  145. falloffcontrast: .25
  146. } )
  147. }
  148. ];
  149. createNoiseGrid( noiseExamples, geometry, font, labelMaterial );
  150. scene.background = hdrTexture;
  151. scene.environment = hdrTexture;
  152. // LIGHTS
  153. particleLight = new THREE.Mesh(
  154. new THREE.SphereGeometry( 0.4, 8, 8 ),
  155. new THREE.MeshBasicMaterial( { color: 0xffffff } )
  156. );
  157. scene.add( particleLight );
  158. particleLight.add( new THREE.PointLight( 0xffffff, 1000 ) );
  159. renderer = new THREE.WebGPURenderer( { antialias: true } );
  160. renderer.setAnimationLoop( render );
  161. renderer.setPixelRatio( window.devicePixelRatio );
  162. renderer.setSize( window.innerWidth, window.innerHeight );
  163. renderer.inspector = new Inspector();
  164. container.appendChild( renderer.domElement );
  165. //
  166. renderer.toneMapping = THREE.ACESFilmicToneMapping;
  167. renderer.toneMappingExposure = 1.25;
  168. // EVENTS
  169. new OrbitControls( camera, renderer.domElement );
  170. window.addEventListener( 'resize', onWindowResize );
  171. }
  172. //
  173. function onWindowResize() {
  174. const width = window.innerWidth;
  175. const height = window.innerHeight;
  176. camera.aspect = width / height;
  177. camera.updateProjectionMatrix();
  178. renderer.setSize( width, height );
  179. }
  180. //
  181. function render() {
  182. const timer = Date.now() * 0.00025;
  183. particleLight.position.x = Math.sin( timer * 7 ) * 30;
  184. particleLight.position.y = Math.cos( timer * 5 ) * 40;
  185. particleLight.position.z = Math.cos( timer * 3 ) * 30;
  186. for ( let i = 0; i < spheres.length; i ++ ) {
  187. const child = spheres[ i ];
  188. child.rotation.y += 0.005;
  189. }
  190. renderer.render( scene, camera );
  191. }
  192. function createNoiseGrid( noiseExamples, geometry, font, labelMaterial ) {
  193. const columns = 7;
  194. const spacingX = 15.5;
  195. const spacingY = 18;
  196. const rowOffset = ( Math.ceil( noiseExamples.length / columns ) - 1 ) * spacingY * .5;
  197. const columnOffset = ( columns - 1 ) * spacingX * .5;
  198. const errorMaterial = new THREE.MeshBasicMaterial( { color: 0xff3333 } );
  199. for ( let i = 0; i < noiseExamples.length; i ++ ) {
  200. const example = noiseExamples[ i ];
  201. const column = i % columns;
  202. const row = Math.floor( i / columns );
  203. const x = column * spacingX - columnOffset;
  204. const y = rowOffset - row * spacingY;
  205. const labelLines = example.label.slice();
  206. const material = new THREE.MeshPhysicalNodeMaterial();
  207. try {
  208. material.colorNode = example.createNode();
  209. } catch ( error ) {
  210. console.error( `Failed to create MaterialX noise sample: ${ example.label.join( ' ' ) }`, error );
  211. labelLines.push( 'Failed' );
  212. }
  213. const mesh = new THREE.Mesh( geometry, material.colorNode ? material : errorMaterial );
  214. mesh.position.set( x, y, 0 );
  215. group.add( mesh );
  216. spheres.push( mesh );
  217. const label = createLabel( labelLines, font, labelMaterial );
  218. label.position.set( x, y - 7.5, 0 );
  219. group.add( label );
  220. }
  221. }
  222. function createLabel( lines, font, material ) {
  223. const label = new THREE.Group();
  224. const lineHeight = 1.55;
  225. const startY = ( lines.length - 1 ) * lineHeight * .5;
  226. for ( let i = 0; i < lines.length; i ++ ) {
  227. const textGeometry = new TextGeometry( lines[ i ], {
  228. font: font,
  229. size: 1.15,
  230. depth: 0.02,
  231. curveSegments: 2
  232. } );
  233. textGeometry.computeBoundingBox();
  234. const boundingBox = textGeometry.boundingBox;
  235. const offsetX = - 0.5 * ( boundingBox.max.x - boundingBox.min.x );
  236. const offsetY = - 0.5 * ( boundingBox.max.y - boundingBox.min.y );
  237. textGeometry.translate( offsetX, offsetY, 0 );
  238. const textMesh = new THREE.Mesh( textGeometry, material );
  239. textMesh.position.y = startY - i * lineHeight;
  240. label.add( textMesh );
  241. }
  242. return label;
  243. }
  244. </script>
  245. </body>
  246. </html>
粤ICP备19079148号